Repository: apple/pkl
File: pkl-parser/src/main/java/org/pkl/parser/Parser.java (Java)

Code before:

  private Type parseTypeAtom(@Nullable String expectation) {
    Type typ;
    switch (lookahead) {
      case UNKNOWN -> typ = new Type.UnknownType(next().span);
      case NOTHING -> typ = new Type.NothingType(next().span);
      case MODULE -> typ = new Type.ModuleType(next().span);
      case LPAREN -> {
        var tk = next();
        var children = new ArrayList<Node>();
        Span end;
        if (lookahead == Token.RPAREN) {
          end = next().span;
        } else {
          children.addAll(parseListOf(Token.COMMA, Token.RPAREN, () -> parseType(")")));
          end = expect(Token.RPAREN, "unexpectedToken2", ",", ")").span;
        }
        if (lookahead == Token.ARROW || children.size() > 1) {
          expect(Token.ARROW, "unexpectedToken", "->");
          var ret = parseType(expectation);
          children.add(ret);
          typ = new Type.FunctionType(children, tk.span.endWith(ret.span()));
        } else {
          if (children.isEmpty()) {
            backtrack();
            throw parserError("unexpectedTokenForType", ")");
          }
          typ = new ParenthesizedType((Type) children.get(0), tk.span.endWith(end));
        }
      }
      case IDENTIFIER -> {
        var start = spanLookahead;
        var name = parseQualifiedIdentifier();
        var end = name.span();
        TypeArgumentList typeArgumentList = null;
        if (lookahead == Token.LT) {
          typeArgumentList = parseTypeArgumentList();
          end = typeArgumentList.span();
        }
        typ = new DeclaredType(name, typeArgumentList, start.endWith(end));
      }
      case STRING_START -> {
        var str = parseStringConstant();
        typ = new StringConstantType(str, str.span());
      }
      default -> {
        var text = _lookahead.text(lexer);
        if (expectation != null) {
          throw parserError("unexpectedTokenForType2", text, expectation);
        }
        throw parserError("unexpectedTokenForType", text);

Review comment (suggestion):
```suggestion
          if (children.isEmpty()) {
            throw parserError("unexpectedTokenForType", ")");
          }
```
You don't need to backtrack if you are going to throw an error.

Code after:

  private Type parseTypeAtom(@Nullable String expectation) {
    Type typ;
    switch (lookahead) {
      case UNKNOWN -> typ = new Type.UnknownType(next().span);
      case NOTHING -> typ = new Type.NothingType(next().span);
      case MODULE -> typ = new Type.ModuleType(next().span);
      case LPAREN -> {
        var tk = next();
        var children = new ArrayList<Node>();
        Span end;
        if (lookahead == Token.RPAREN) {
          end = next().span;
        } else {
          children.addAll(parseListOf(Token.COMMA, Token.RPAREN, () -> parseType(")")));
          end = expect(Token.RPAREN, "unexpectedToken2", ",", ")").span;
        }
        if (lookahead == Token.ARROW || children.size() > 1) {
          expect(Token.ARROW, "unexpectedToken", "->");
          var ret = parseType(expectation);
          children.add(ret);
          typ = new Type.FunctionType(children, tk.span.endWith(ret.span()));
        } else {
          if (children.isEmpty()) {
            throw new ParserError(ErrorMessages.create("unexpectedTokenForType", ")"), end);
          }
          typ = new ParenthesizedType((Type) children.get(0), tk.span.endWith(end));
        }
      }
      case IDENTIFIER -> {
        var start = spanLookahead;
        var name = parseQualifiedIdentifier();
        var end = name.span();
        TypeArgumentList typeArgumentList = null;
        if (lookahead == Token.LT) {
          typeArgumentList = parseTypeArgumentList();
          end = typeArgumentList.span();
        }
        typ = new DeclaredType(name, typeArgumentList, start.endWith(end));
      }
      case STRING_START -> {
        var str = parseStringConstant();
        typ = new StringConstantType(str, str.span());
      }
      default -> {
        var text = _lookahead.text(lexer);
        if (expectation != null) {
          throw parserError("unexpectedTokenForType2", text, expectation);
        }
        throw parserError("unexpectedTokenForType", text);
      }