content: /*
   * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package openjdk.tools.javac.parser;
  
  import openjdk.source.tree.CaseTree;
  import openjdk.source.tree.MemberReferenceTree.ReferenceMode;
  import openjdk.source.tree.ModuleTree.ModuleKind;
  import openjdk.tools.javac.code.BoundKind;
  import openjdk.tools.javac.code.Flags;
  import openjdk.tools.javac.code.Preview;
  import openjdk.tools.javac.code.Source;
  import openjdk.tools.javac.code.Source.Feature;
  import openjdk.tools.javac.code.TypeTag;
  import openjdk.tools.javac.parser.Tokens.Comment;
  import openjdk.tools.javac.parser.Tokens.Comment.CommentStyle;
  import openjdk.tools.javac.parser.Tokens.Token;
  import openjdk.tools.javac.parser.Tokens.TokenKind;
  import openjdk.tools.javac.resources.CompilerProperties.Errors;
  import openjdk.tools.javac.resources.CompilerProperties.Fragments;
  import openjdk.tools.javac.resources.CompilerProperties.Warnings;
  import openjdk.tools.javac.tree.DocCommentTable;
  import openjdk.tools.javac.tree.EndPosTable;
  import openjdk.tools.javac.tree.JCTree;
  import openjdk.tools.javac.tree.JCTree.JCAnnotatedType;
  import openjdk.tools.javac.tree.JCTree.JCAnnotation;
  import openjdk.tools.javac.tree.JCTree.JCArrayTypeTree;
  import openjdk.tools.javac.tree.JCTree.JCAssert;
  import openjdk.tools.javac.tree.JCTree.JCBinary;
  import openjdk.tools.javac.tree.JCTree.JCBlock;
  import openjdk.tools.javac.tree.JCTree.JCBreak;
  import openjdk.tools.javac.tree.JCTree.JCCase;
  import openjdk.tools.javac.tree.JCTree.JCCaseLabel;
  import openjdk.tools.javac.tree.JCTree.JCCatch;
  import openjdk.tools.javac.tree.JCTree.JCClassDecl;
  import openjdk.tools.javac.tree.JCTree.JCContinue;
  import openjdk.tools.javac.tree.JCTree.JCDirective;
  import openjdk.tools.javac.tree.JCTree.JCDoWhileLoop;
  import openjdk.tools.javac.tree.JCTree.JCErroneous;
  import openjdk.tools.javac.tree.JCTree.JCExpression;
  import openjdk.tools.javac.tree.JCTree.JCExpressionStatement;
  import openjdk.tools.javac.tree.JCTree.JCFieldAccess;
  import openjdk.tools.javac.tree.JCTree.JCIdent;
  import openjdk.tools.javac.tree.JCTree.JCImport;
  import openjdk.tools.javac.tree.JCTree.JCLiteral;
  import openjdk.tools.javac.tree.JCTree.JCMemberReference;
  import openjdk.tools.javac.tree.JCTree.JCMethodDecl;
  import openjdk.tools.javac.tree.JCTree.JCModifiers;
  import openjdk.tools.javac.tree.JCTree.JCModuleDecl;
  import openjdk.tools.javac.tree.JCTree.JCNewArray;
  import openjdk.tools.javac.tree.JCTree.JCNewClass;
  import openjdk.tools.javac.tree.JCTree.JCPackageDecl;
  import openjdk.tools.javac.tree.JCTree.JCPattern;
  import openjdk.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
  import openjdk.tools.javac.tree.JCTree.JCReturn;
  import openjdk.tools.javac.tree.JCTree.JCStatement;
  import openjdk.tools.javac.tree.JCTree.JCSwitch;
  import openjdk.tools.javac.tree.JCTree.JCSwitchExpression;
  import openjdk.tools.javac.tree.JCTree.JCThrow;
  import openjdk.tools.javac.tree.JCTree.JCTypeApply;
  import openjdk.tools.javac.tree.JCTree.JCTypeParameter;
  import openjdk.tools.javac.tree.JCTree.JCVariableDecl;
  import openjdk.tools.javac.tree.JCTree.Tag;
  import openjdk.tools.javac.tree.JCTree.TypeBoundKind;
  import openjdk.tools.javac.tree.TreeInfo;
  import openjdk.tools.javac.tree.TreeMaker;
  import openjdk.tools.javac.util.Assert;
  import openjdk.tools.javac.util.Convert;
  import openjdk.tools.javac.util.IntHashTable;
  import openjdk.tools.javac.util.JCDiagnostic;
  import openjdk.tools.javac.util.JCDiagnostic.DiagnosticFlag;
  import openjdk.tools.javac.util.JCDiagnostic.Error;
  import openjdk.tools.javac.util.JCDiagnostic.Fragment;
  import openjdk.tools.javac.util.List;
  import openjdk.tools.javac.util.ListBuffer;
  import openjdk.tools.javac.util.Log;
  import openjdk.tools.javac.util.Name;
  import openjdk.tools.javac.util.Names;
  import openjdk.tools.javac.util.Position;
  
  import java.util.ArrayList;
  import java.util.function.Function;
  import java.util.function.Predicate;
  import java.util.stream.Collectors;
  
  import static openjdk.tools.javac.parser.Tokens.TokenKind.AMP;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.AMPAMP;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.ARROW;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.ASSERT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.BAR;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.CASE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.CATCH;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.CLASS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.COLCOL;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.COLON;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.COMMA;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.DEFAULT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.DOT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.ELLIPSIS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.ELSE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.ENUM;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.EOF;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.EQ;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.EXTENDS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.FINAL;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.FINALLY;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.GT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.GTGTGTEQ;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.IDENTIFIER;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.IMPLEMENTS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.IMPORT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.INSTANCEOF;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.INTERFACE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.INTLITERAL;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.LBRACE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.LBRACKET;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.LONGLITERAL;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.LPAREN;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.LT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.NEW;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.PACKAGE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.PLUSEQ;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.PLUSPLUS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.QUES;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.RBRACE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.RBRACKET;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.RPAREN;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.SEMI;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.STAR;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.STATIC;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.SUB;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.SUBSUB;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.SUPER;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.THIS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.THROWS;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.TRUE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.UNDERSCORE;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.VOID;
  import static openjdk.tools.javac.parser.Tokens.TokenKind.WHILE;
  import static openjdk.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
  import static openjdk.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
  import static openjdk.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
  import static openjdk.tools.javac.tree.JCTree.Tag.AND;
  import static openjdk.tools.javac.tree.JCTree.Tag.ANNOTATED_TYPE;
  import static openjdk.tools.javac.tree.JCTree.Tag.BITAND;
  import static openjdk.tools.javac.tree.JCTree.Tag.BITAND_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.BITOR;
  import static openjdk.tools.javac.tree.JCTree.Tag.BITOR_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.BITXOR;
  import static openjdk.tools.javac.tree.JCTree.Tag.BITXOR_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.BLOCK;
  import static openjdk.tools.javac.tree.JCTree.Tag.COMPL;
  import static openjdk.tools.javac.tree.JCTree.Tag.DIV;
  import static openjdk.tools.javac.tree.JCTree.Tag.DIV_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.ERRONEOUS;
  import static openjdk.tools.javac.tree.JCTree.Tag.EXEC;
  import static openjdk.tools.javac.tree.JCTree.Tag.GE;
  import static openjdk.tools.javac.tree.JCTree.Tag.IDENT;
  import static openjdk.tools.javac.tree.JCTree.Tag.LE;
  import static openjdk.tools.javac.tree.JCTree.Tag.LITERAL;
  import static openjdk.tools.javac.tree.JCTree.Tag.METHODDEF;
  import static openjdk.tools.javac.tree.JCTree.Tag.MINUS;
  import static openjdk.tools.javac.tree.JCTree.Tag.MINUS_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.MOD;
  import static openjdk.tools.javac.tree.JCTree.Tag.MOD_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.MUL;
  import static openjdk.tools.javac.tree.JCTree.Tag.MUL_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.NE;
  import static openjdk.tools.javac.tree.JCTree.Tag.NEG;
  import static openjdk.tools.javac.tree.JCTree.Tag.NOT;
  import static openjdk.tools.javac.tree.JCTree.Tag.NO_TAG;
  import static openjdk.tools.javac.tree.JCTree.Tag.OR;
  import static openjdk.tools.javac.tree.JCTree.Tag.PLUS_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.POS;
  import static openjdk.tools.javac.tree.JCTree.Tag.POSTDEC;
  import static openjdk.tools.javac.tree.JCTree.Tag.POSTINC;
  import static openjdk.tools.javac.tree.JCTree.Tag.PREDEC;
  import static openjdk.tools.javac.tree.JCTree.Tag.PREINC;
  import static openjdk.tools.javac.tree.JCTree.Tag.SELECT;
  import static openjdk.tools.javac.tree.JCTree.Tag.SL;
  import static openjdk.tools.javac.tree.JCTree.Tag.SL_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.SR;
  import static openjdk.tools.javac.tree.JCTree.Tag.SR_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.TYPEAPPLY;
  import static openjdk.tools.javac.tree.JCTree.Tag.TYPEARRAY;
  import static openjdk.tools.javac.tree.JCTree.Tag.TYPETEST;
  import static openjdk.tools.javac.tree.JCTree.Tag.USR;
  import static openjdk.tools.javac.tree.JCTree.Tag.USR_ASG;
  import static openjdk.tools.javac.tree.JCTree.Tag.VARDEF;
  
  /**
   * The parser maps a token sequence into an abstract syntax tree. The parser is
   * a hand-written recursive-descent parser that implements the grammar described
   * in the Java Language Specification. For efficiency reasons, an operator
   * precedence scheme is used for parsing binary operation expressions.
   *
   * <p>
   * <b>This is NOT part of any supported API. If you write code that depends on
   * this, you do so at your own risk. This code and its internal interfaces are
   * subject to change or deletion without notice.</b>
   */
  public class JavacParser implements Parser {
  
      /**
       * The number of precedence levels of infix operators.
       */
      private static final int infixPrecedenceLevels = 10;
  
      /**
       * Is the parser instantiated to parse a module-info file ?
       */
      private final boolean parseModuleInfo;
  
      /**
       * The scanner used for lexical analysis.
       */
      protected Lexer S;
  
      /**
       * The factory to be used for abstract syntax tree construction.
       */
      protected TreeMaker F;
  
      /**
       * The log to be used for error diagnostics.
       */
      protected Log log;
  
      /**
       * The Source language setting.
       */
      protected Source source;
  
      /**
       * The Preview language setting.
       */
      protected Preview preview;
  
      /**
       * The name table.
       */
      protected Names names;
  
      /**
       * End position mappings container
       */
      protected final AbstractEndPosTable endPosTable;
  
      // Because of javac's limited lookahead, some contexts are ambiguous in
      // the presence of type annotations even though they are not ambiguous
      // in the absence of type annotations.  Consider this code:
      //   void m(String [] m) { }
      //   void m(String ... m) { }
      // After parsing "String", javac calls bracketsOpt which immediately
      // returns if the next character is not '['.  Similarly, javac can see
      // if the next token is ... and in that case parse an ellipsis.  But in
      // the presence of type annotations:
      //   void m(String @A [] m) { }
      //   void m(String @A ... m) { }
      // no finite lookahead is enough to determine whether to read array
      // levels or an ellipsis.  Furthermore, if you call bracketsOpt, then
      // bracketsOpt first reads all the leading annotations and only then
      // discovers that it needs to fail.  bracketsOpt needs a way to push
      // back the extra annotations that it read.  (But, bracketsOpt should
      // not *always* be allowed to push back extra annotations that it finds
      // -- in most contexts, any such extra annotation is an error.
      //
      // The following two variables permit type annotations that have
      // already been read to be stored for later use.  Alternate
      // implementations are possible but would cause much larger changes to
      // the parser.
      /**
       * Type annotations that have already been read but have not yet been used. *
       */
      protected List<JCAnnotation> typeAnnotationsPushedBack = List.nil();
  
      /**
       * If the parser notices extra annotations, then it either immediately
       * issues an error (if this variable is false) or places the extra
       * annotations in variable typeAnnotationsPushedBack (if this variable is
       * true).
       */
      protected boolean permitTypeAnnotationsPushBack = false;
  
      protected interface ErrorRecoveryAction {
  
          JCTree doRecover(JavacParser parser);
      }
  
      protected enum BasicErrorRecoveryAction implements ErrorRecoveryAction {
          BLOCK_STMT {
              public JCTree doRecover(JavacParser parser) {
                  return parser.parseStatementAsBlock();
              }
          },
          CATCH_CLAUSE {
              public JCTree doRecover(JavacParser parser) {
                  return parser.catchClause();
              }
          }
      }
  
      /**
       * Construct a parser from a given scanner, tree factory and log.
       */
      protected JavacParser(ParserFactory fac,
                            Lexer S,
                            boolean keepDocComments,
                            boolean keepLineMap,
                            boolean keepEndPositions) {
          this(fac, S, keepDocComments, keepLineMap, keepEndPositions, false);
  
      }
  
      /**
       * Construct a parser from a given scanner, tree factory and log.
       */
      protected JavacParser(ParserFactory fac,
                            Lexer S,
                            boolean keepDocComments,
                            boolean keepLineMap,
                            boolean keepEndPositions,
                            boolean parseModuleInfo) {
          this.S = S;
          nextToken(); // prime the pump
          this.F = fac.F;
          this.log = fac.log;
          this.names = fac.names;
          this.source = fac.source;
          this.preview = fac.preview;
          this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true);
          this.keepDocComments = keepDocComments;
          this.parseModuleInfo = this.stopAtModule = parseModuleInfo;
          this.keepLineMap = keepLineMap;
          this.errorTree = F.Erroneous();
          this.endPosTable = newEndPosTable(keepEndPositions);
  
          this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source);
          this.docComments = newDocCommentTable(keepDocComments, fac, endPosTable);
          this.allowRecords = Feature.RECORDS.allowedInSource(source);
          this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
  
      }
  
      protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
          return keepEndPositions
                  ? new SimpleEndPosTable(this)
                  : new EmptyEndPosTable(this);
      }
  
      protected DocCommentTable newDocCommentTable(boolean keepDocComments, ParserFactory fac, EndPosTable ept) {
          return keepDocComments ? new LazyDocCommentTable(fac, ept) : null;
      }
  
      /**
       * Switch: should we fold strings?
       */
      protected boolean allowStringFolding;
  
      /**
       * Switch: should we keep docComments?
       */
      protected boolean keepDocComments;
  
      /**
       * Switch: should we keep line table?
       */
      protected boolean keepLineMap;
  
      /**
       * Switch: is "this" allowed as an identifier? This is needed to parse
       * receiver types.
       */
      protected boolean allowThisIdent;
  
      /**
       * Switch: is yield statement allowed in this source level?
       */
      protected boolean allowYieldStatement;
  
      /**
       * Switch: are records allowed in this source level?
       */
      protected boolean allowRecords;
  
      /**
       * Switch: are sealed types allowed in this source level?
       */
      protected boolean allowSealedTypes;
      /**
       * The type of the method receiver, as specified by a first "this"
       * parameter.
       */
      protected JCVariableDecl receiverParam;
  
      /**
       * When terms are parsed, the mode determines which is expected: mode = EXPR
       * : an expression mode = TYPE : a type mode = NOPARAMS : no parameters
       * allowed for type mode = TYPEARG : type argument mode |= NOLAMBDA :
       * lambdas are not allowed
       */
      protected static final int EXPR = 0x1;
      protected static final int TYPE = 0x2;
      protected static final int NOPARAMS = 0x4;
      protected static final int TYPEARG = 0x8;
      protected static final int DIAMOND = 0x10;
      protected static final int NOLAMBDA = 0x20;
  
      protected void selectExprMode() {
          mode = (mode & NOLAMBDA) | EXPR;
      }
  
      protected void selectTypeMode() {
          mode = (mode & NOLAMBDA) | TYPE;
      }
  
      /**
       * The current mode.
       */
      protected int mode = 0;
  
      /**
       * The mode of the term that was parsed last.
       */
      protected int lastmode = 0;
  
      /* ---------- token management -------------- */
      protected Token token;
  
      public Token token() {
          return token;
      }
  
      public void nextToken() {
          S.nextToken();
          token = S.token();
      }
  
      protected boolean peekToken(Predicate<TokenKind> tk) {
          return peekToken(0, tk);
      }
  
      protected boolean peekToken(int lookahead, Predicate<TokenKind> tk) {
          return tk.test(S.token(lookahead + 1).kind);
      }
  
      protected boolean peekToken(Predicate<TokenKind> tk1, Predicate<TokenKind> tk2) {
          return peekToken(0, tk1, tk2);
      }
  
      protected boolean peekToken(int lookahead, Predicate<TokenKind> tk1, Predicate<TokenKind> tk2) {
          return tk1.test(S.token(lookahead + 1).kind)
                  && tk2.test(S.token(lookahead + 2).kind);
      }
  
      protected boolean peekToken(Predicate<TokenKind> tk1, Predicate<TokenKind> tk2, Predicate<TokenKind> tk3) {
          return peekToken(0, tk1, tk2, tk3);
      }
  
      protected boolean peekToken(int lookahead, Predicate<TokenKind> tk1, Predicate<TokenKind> tk2, Predicate<TokenKind> tk3) {
          return tk1.test(S.token(lookahead + 1).kind)
                  && tk2.test(S.token(lookahead + 2).kind)
                  && tk3.test(S.token(lookahead + 3).kind);
      }
  
      @SuppressWarnings("unchecked")
      protected boolean peekToken(Predicate<TokenKind>... kinds) {
          return peekToken(0, kinds);
      }
  
      @SuppressWarnings("unchecked")
      protected boolean peekToken(int lookahead, Predicate<TokenKind>... kinds) {
          for (Predicate<TokenKind> kind : kinds) {
              if (!kind.test(S.token(++lookahead).kind)) {
                  return false;
              }
          }
          return true;
      }
  
      /* ---------- error recovery -------------- */
      private JCErroneous errorTree;
      private boolean stopAtModule;
  
      /**
       * Skip forward until a suitable stop token is found.
       */
      protected void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) {
          while (true) {
              switch (token.kind) {
                  case SEMI:
                      nextToken();
                      return;
                  case PUBLIC:
                  case FINAL:
                  case ABSTRACT:
                  case MONKEYS_AT:
                  case EOF:
                  case CLASS:
                  case INTERFACE:
                  case ENUM:
                      return;
                  case IMPORT:
                  case PACKAGE:
                      if (stopAtImport) {
                          return;
                      }
                      break;
                  case LBRACE:
                  case RBRACE:
                  case PRIVATE:
                  case PROTECTED:
                  case STATIC:
                  case TRANSIENT:
                  case NATIVE:
                  case VOLATILE:
                  case SYNCHRONIZED:
                  case STRICTFP:
                  case BYTE:
                  case SHORT:
                  case CHAR:
                  case INT:
                  case LONG:
                  case FLOAT:
                  case DOUBLE:
                  case BOOLEAN:
                  case VOID:
                      if (stopAtMemberDecl) {
                          return;
                      }
                      break;
                  case UNDERSCORE:
                  case IDENTIFIER:
                      if (stopAtModule && token.name() == names.module) {
                          return;
                      }
                      if (stopAtIdentifier) {
                          return;
                      }
                      break;
                  case CASE:
                  case DEFAULT:
                  case IF:
                  case FOR:
                  case WHILE:
                  case DO:
                  case TRY:
                  case SWITCH:
                  case RETURN:
                  case THROW:
                  case BREAK:
                  case CONTINUE:
                  case ELSE:
                  case FINALLY:
                  case CATCH:
                  case THIS:
                  case SUPER:
                  case NEW:
                      if (stopAtStatement) {
                          return;
                      }
                      break;
                  case ASSERT:
                      if (stopAtStatement) {
                          return;
                      }
                      break;
              }
              nextToken();
          }
      }
  
      protected JCErroneous syntaxError(int pos, Error errorKey) {
          return syntaxError(pos, List.nil(), errorKey);
      }
  
      protected JCErroneous syntaxError(int pos, List<JCTree> errs, Error errorKey) {
          setErrorEndPos(pos);
          JCErroneous err = F.at(pos).Erroneous(errs);
          reportSyntaxError(err, errorKey);
          if (errs != null) {
              JCTree last = errs.last();
              if (last != null) {
                  storeEnd(last, pos);
              }
          }
          return toP(err);
      }
  
      private static final int RECOVERY_THRESHOLD = 50;
      private int errorPos = Position.NOPOS;
      private int count = 0;
  
      /**
       * Report a syntax using the given the position parameter and arguments,
       * unless one was already reported at the same position.
       */
      protected void reportSyntaxError(int pos, Error errorKey) {
          JCDiagnostic.DiagnosticPosition diag = new JCDiagnostic.SimpleDiagnosticPosition(pos);
          reportSyntaxError(diag, errorKey);
      }
  
      /**
       * Report a syntax error using the given DiagnosticPosition object and
       * arguments, unless one was already reported at the same position.
       */
      protected void reportSyntaxError(JCDiagnostic.DiagnosticPosition diagPos, Error errorKey) {
          int pos = diagPos.getPreferredPosition();
          if (pos > S.errPos() || pos == Position.NOPOS) {
              if (token.kind == EOF) {
                  log.error(DiagnosticFlag.SYNTAX, diagPos, Errors.PrematureEof);
              } else {
                  log.error(DiagnosticFlag.SYNTAX, diagPos, errorKey);
              }
          }
          S.errPos(pos);
          if (token.pos == errorPos && token.kind != EOF) {
              //check for a possible infinite loop in parsing:
              Assert.check(count++ < RECOVERY_THRESHOLD);
          } else {
              count = 0;
              errorPos = token.pos;
          }
      }
  
      /**
       * If next input token matches given token, skip it, otherwise report an
       * error.
       */
      public void accept(TokenKind tk) {
          accept(tk, Errors::Expected);
      }
  
      /**
       * If next input token matches given token, skip it, otherwise report an
       * error.
       */
      public void accept(TokenKind tk, Function<TokenKind, Error> errorProvider) {
          if (token.kind == tk) {
              nextToken();
          } else {
              setErrorEndPos(token.pos);
              reportSyntaxError(S.prevToken().endPos, errorProvider.apply(tk));
          }
      }
  
      /**
       * Report an illegal start of expression/type error at given position.
       */
      JCExpression illegal(int pos) {
          setErrorEndPos(pos);
          if ((mode & EXPR) != 0) {
              return syntaxError(pos, Errors.IllegalStartOfExpr);
          } else {
              return syntaxError(pos, Errors.IllegalStartOfType);
          }
  
      }
  
      /**
       * Report an illegal start of expression/type error at current position.
       */
      JCExpression illegal() {
          return illegal(token.pos);
      }
  
      /**
       * Diagnose a modifier flag from the set, if any.
       */
      protected void checkNoMods(long mods) {
          checkNoMods(token.pos, mods);
      }
  
      protected void checkNoMods(int pos, long mods) {
          if (mods != 0) {
              long lowestMod = mods & -mods;
              log.error(DiagnosticFlag.SYNTAX, pos, Errors.ModNotAllowedHere(Flags.asFlagSet(lowestMod)));
          }
      }
  
      /* ---------- doc comments --------- */
      /**
       * A table to store all documentation comments indexed by the tree nodes
       * they refer to. defined only if option flag keepDocComment is set.
       */
      private final DocCommentTable docComments;
  
      /**
       * Make an entry into docComments hashtable, provided flag keepDocComments
       * is set and given doc comment is non-null.
       *
       * @param tree The tree to be used as index in the hashtable
       * @param dc   The doc comment to associate with the tree, or null.
       */
      protected void attach(JCTree tree, Comment dc) {
          if (keepDocComments && dc != null) {
  //          System.out.println("doc comment = ");System.out.println(dc);//DEBUG
              docComments.putComment(tree, dc);
          }
      }
  
      public DocCommentTable getDocComments() {
          return docComments;
      }
  
      /* -------- source positions ------- */
      protected void setErrorEndPos(int errPos) {
          endPosTable.setErrorEndPos(errPos);
      }
  
      protected void storeEnd(JCTree tree, int endpos) {
          endPosTable.storeEnd(tree, endpos);
      }
  
      protected <T extends JCTree> T to(T t) {
          return endPosTable.to(t);
      }
  
      protected <T extends JCTree> T toP(T t) {
          return endPosTable.toP(t);
      }
  
      /**
       * Get the start position for a tree node. The start position is defined to
       * be the position of the first character of the first token of the node's
       * source text.
       *
       * @param tree The tree node
       */
      public int getStartPos(JCTree tree) {
          return TreeInfo.getStartPos(tree);
      }
  
      /**
       * Get the end position for a tree node. The end position is defined to be
       * the position of the last character of the last token of the node's source
       * text. Returns Position.NOPOS if end positions are not generated or the
       * position is otherwise not found.
       *
       * @param tree The tree node
       */
      public int getEndPos(JCTree tree) {
          return TreeInfo.getEndPos(tree, endPosTable);
      }
  
      /* ---------- parsing -------------- */
  
      /**
       * Ident = IDENTIFIER
       */
      public Name ident() {
          return ident(false);
      }
  
      protected Name ident(boolean allowClass) {
          if (token.kind == IDENTIFIER) {
              Name name = token.name();
              nextToken();
              return name;
          } else if (token.kind == ASSERT) {
              log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.AssertAsIdentifier);
              nextToken();
              return names.error;
          } else if (token.kind == ENUM) {
              log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.EnumAsIdentifier);
              nextToken();
              return names.error;
          } else if (token.kind == THIS) {
              if (allowThisIdent) {
                  // Make sure we're using a supported source version.
                  checkSourceLevel(Feature.TYPE_ANNOTATIONS);
                  Name name = token.name();
                  nextToken();
                  return name;
              } else {
                  log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.ThisAsIdentifier);
                  nextToken();
                  return names.error;
              }
          } else if (token.kind == UNDERSCORE) {
              if (Feature.UNDERSCORE_IDENTIFIER.allowedInSource(source)) {
                  log.warning(token.pos, Warnings.UnderscoreAsIdentifier);
              } else {
                  log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.UnderscoreAsIdentifier);
              }
              Name name = token.name();
              nextToken();
              return name;
          } else {
              accept(IDENTIFIER);
              if (allowClass && token.kind == CLASS) {
                  nextToken();
                  return names._class;
              }
              return names.error;
          }
      }
  
      /**
       * Qualident = Ident { DOT [Annotations] Ident }
       */
      public JCExpression qualident(boolean allowAnnos) {
          JCExpression t = toP(F.at(token.pos).Ident(ident()));
          while (token.kind == DOT) {
              int pos = token.pos;
              nextToken();
              List<JCAnnotation> tyannos = null;
              if (allowAnnos) {
                  tyannos = typeAnnotationsOpt();
              }
              t = toP(F.at(pos).Select(t, ident()));
              if (tyannos != null && tyannos.nonEmpty()) {
                  t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
              }
          }
          return t;
      }
  
      JCExpression literal(Name prefix) {
          return literal(prefix, token.pos);
      }
  
      /**
       * Literal = INTLITERAL | LONGLITERAL | FLOATLITERAL | DOUBLELITERAL |
       * CHARLITERAL | STRINGLITERAL | TRUE | FALSE | NULL
       */
      JCExpression literal(Name prefix, int pos) {
          JCExpression t = errorTree;
          switch (token.kind) {
              case INTLITERAL:
                  try {
                      t = F.at(pos).Literal(
                              TypeTag.INT,
                              Convert.string2int(strval(prefix), token.radix()));
                  } catch (NumberFormatException ex) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.IntNumberTooLarge(strval(prefix)));
                  }
                  break;
              case LONGLITERAL:
                  try {
                      t = F.at(pos).Literal(
                              TypeTag.LONG,
                              Long.valueOf(Convert.string2long(strval(prefix), token.radix())));
                  } catch (NumberFormatException ex) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.IntNumberTooLarge(strval(prefix)));
                  }
                  break;
              case FLOATLITERAL: {
                  String proper = token.radix() == 16
                          ? ("0x" + token.stringVal())
                          : token.stringVal();
                  Float n;
                  try {
                      n = Float.valueOf(proper);
                  } catch (NumberFormatException ex) {
                      // error already reported in scanner
                      n = Float.NaN;
                  }
                  if (n.floatValue() == 0.0f && !isZero(proper)) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.FpNumberTooSmall);
                  } else if (n.floatValue() == Float.POSITIVE_INFINITY) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.FpNumberTooLarge);
                  } else {
                      t = F.at(pos).Literal(TypeTag.FLOAT, n);
                  }
                  break;
              }
              case DOUBLELITERAL: {
                  String proper = token.radix() == 16
                          ? ("0x" + token.stringVal())
                          : token.stringVal();
                  Double n;
                  try {
                      n = Double.valueOf(proper);
                  } catch (NumberFormatException ex) {
                      // error already reported in scanner
                      n = Double.NaN;
                  }
                  if (n.doubleValue() == 0.0d && !isZero(proper)) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.FpNumberTooSmall);
                  } else if (n.doubleValue() == Double.POSITIVE_INFINITY) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.FpNumberTooLarge);
                  } else {
                      t = F.at(pos).Literal(TypeTag.DOUBLE, n);
                  }
                  break;
              }
              case CHARLITERAL:
                  t = F.at(pos).Literal(
                          TypeTag.CHAR,
                          token.stringVal().charAt(0) + 0);
                  break;
              case STRINGLITERAL:
                  t = F.at(pos).Literal(
                          TypeTag.CLASS,
                          token.stringVal());
                  break;
              case TRUE:
              case FALSE:
                  t = F.at(pos).Literal(
                          TypeTag.BOOLEAN,
                          (token.kind == TRUE ? 1 : 0));
                  break;
              case NULL:
                  t = F.at(pos).Literal(
                          TypeTag.BOT,
                          null);
                  break;
              default:
                  Assert.error();
          }
          if (t == errorTree) {
              t = F.at(pos).Erroneous();
          }
          storeEnd(t, token.endPos);
          nextToken();
          return t;
      }
  
      //where
      boolean isZero(String s) {
          char[] cs = s.toCharArray();
          int base = ((cs.length > 1 && Character.toLowerCase(cs[1]) == 'x') ? 16 : 10);
          int i = ((base == 16) ? 2 : 0);
          while (i < cs.length && (cs[i] == '0' || cs[i] == '.')) {
              i++;
          }
          return !(i < cs.length && (Character.digit(cs[i], base) > 0));
      }
  
      String strval(Name prefix) {
          String s = token.stringVal();
          return prefix.isEmpty() ? s : prefix + s;
      }
  
      /**
       * terms can be either expressions or types.
       */
      public JCExpression parseExpression() {
          return term(EXPR);
      }
  
      /**
       * parses patterns.
       */
      public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType, boolean inInstanceOf) {
          JCPattern pattern;
          if (token.kind == LPAREN && parsedType == null) {
              int startPos = token.pos;
              accept(LPAREN);
              JCPattern p = parsePattern(token.pos, null, null, false);
              accept(RPAREN);
              pattern = toP(F.at(startPos).ParenthesizedPattern(p));
          } else {
              mods = mods != null ? mods : optFinal(0);
              JCExpression e = parsedType == null ? term(TYPE | NOLAMBDA) : parsedType;
              JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
              pattern = toP(F.at(pos).BindingPattern(var));
          }
          if (!inInstanceOf && token.kind == AMPAMP) {
              checkSourceLevel(Feature.PATTERN_SWITCH);
              nextToken();
              JCExpression guard = term(EXPR | NOLAMBDA);
              pattern = F.at(pos).GuardPattern(pattern, guard);
          }
          return pattern;
      }
  
      /**
       * parses (optional) type annotations followed by a type. If the annotations
       * are present before the type and are not consumed during array parsing,
       * this method returns a {@link JCAnnotatedType} consisting of these
       * annotations and the underlying type. Otherwise, it returns the underlying
       * type.
       *
       * <p>
       * <p>
       * Note that this method sets {@code mode} to {@code TYPE} first, before
       * parsing annotations.
       */
      public JCExpression parseType() {
          return parseType(false);
      }
  
      public JCExpression parseType(boolean allowVar) {
          List<JCAnnotation> annotations = typeAnnotationsOpt();
          return parseType(allowVar, annotations);
      }
  
      public JCExpression parseType(boolean allowVar, List<JCAnnotation> annotations) {
          JCExpression result = unannotatedType(allowVar);
  
          if (annotations.nonEmpty()) {
              result = insertAnnotationsToMostInner(result, annotations, false);
          }
  
          return result;
      }
  
      public JCExpression unannotatedType(boolean allowVar) {
          JCExpression result = term(TYPE);
          Name restrictedTypeName = restrictedTypeName(result, !allowVar);
  
          if (restrictedTypeName != null && (!allowVar || restrictedTypeName != names.var)) {
              syntaxError(result.pos, Errors.RestrictedTypeNotAllowedHere(restrictedTypeName));
          }
  
          return result;
      }
  
      protected JCExpression term(int newmode) {
          int prevmode = mode;
          mode = newmode;
          JCExpression t = term();
          lastmode = mode;
          mode = prevmode;
          return t;
      }
  
      /**
       * {@literal
       * Expression = Expression1 [ExpressionRest]
       * ExpressionRest = [AssignmentOperator Expression1]
       * AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" |
       * "&=" | "|=" | "^=" |
       * "%=" | "<<=" | ">>=" | ">>>="
       * Type = Type1
       * TypeNoParams = TypeNoParams1
       * StatementExpression = Expression
       * ConstantExpression = Expression
       * }
       */
      JCExpression term() {
          JCExpression t = term1();
          if ((mode & EXPR) != 0
                  && (token.kind == EQ || PLUSEQ.compareTo(token.kind) <= 0 && token.kind.compareTo(GTGTGTEQ) <= 0)) {
              return termRest(t);
          } else {
              return t;
          }
      }
  
      JCExpression termRest(JCExpression t) {
          switch (token.kind) {
              case EQ: {
                  int pos = token.pos;
                  nextToken();
                  selectExprMode();
                  JCExpression t1 = term();
                  return toP(F.at(pos).Assign(t, t1));
              }
              case PLUSEQ:
              case SUBEQ:
              case STAREQ:
              case SLASHEQ:
              case PERCENTEQ:
              case AMPEQ:
              case BAREQ:
              case CARETEQ:
              case LTLTEQ:
              case GTGTEQ:
              case GTGTGTEQ:
                  int pos = token.pos;
                  TokenKind tk = token.kind;
                  nextToken();
                  selectExprMode();
                  JCExpression t1 = term();
                  return F.at(pos).Assignop(optag(tk), t, t1);
              default:
                  return t;
          }
      }
  
      /**
       * Expression1 = Expression2 [Expression1Rest] Type1 = Type2 TypeNoParams1 =
       * TypeNoParams2
       */
      JCExpression term1() {
          JCExpression t = term2();
          if ((mode & EXPR) != 0 && token.kind == QUES) {
              selectExprMode();
              return term1Rest(t);
          } else {
              return t;
          }
      }
  
      /**
       * Expression1Rest = ["?" Expression ":" Expression1]
       */
      JCExpression term1Rest(JCExpression t) {
          if (token.kind == QUES) {
              int pos = token.pos;
              nextToken();
              JCExpression t1 = term();
              accept(COLON);
              JCExpression t2 = term1();
              return F.at(pos).Conditional(t, t1, t2);
          } else {
              return t;
          }
      }
  
      /**
       * Expression2 = Expression3 [Expression2Rest] Type2 = Type3 TypeNoParams2 =
       * TypeNoParams3
       */
      JCExpression term2() {
          JCExpression t = term3();
          if ((mode & EXPR) != 0 && prec(token.kind) >= TreeInfo.orPrec) {
              selectExprMode();
              return term2Rest(t, TreeInfo.orPrec);
          } else {
              return t;
          }
      }
  
      /*  Expression2Rest = {infixop Expression3}
       *                  | Expression3 instanceof Type
       *                  | Expression3 instanceof Pattern
       *  infixop         = "||"
       *                  | "&&"
       *                  | "|"
       *                  | "^"
       *                  | "&"
       *                  | "==" | "!="
       *                  | "<" | ">" | "<=" | ">="
       *                  | "<<" | ">>" | ">>>"
       *                  | "+" | "-"
       *                  | "*" | "/" | "%"
       */
      JCExpression term2Rest(JCExpression t, int minprec) {
          JCExpression[] odStack = newOdStack();
          Token[] opStack = newOpStack();
  
          // optimization, was odStack = new Tree[...]; opStack = new Tree[...];
          int top = 0;
          odStack[0] = t;
          int startPos = TreeInfo.getStartPos(t);
          Token topOp = Tokens.DUMMY;
          while (prec(token.kind) >= minprec) {
              opStack[top] = topOp;
  
              if (token.kind == INSTANCEOF) {
                  int pos = token.pos;
                  nextToken();
                  JCTree pattern;
                  if (token.kind == LPAREN) {
                      checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
                      pattern = parsePattern(token.pos, null, null, true);
                  } else {
                      int patternPos = token.pos;
                      JCModifiers mods = optFinal(0);
                      int typePos = token.pos;
                      JCExpression type = unannotatedType(false);
                      if (token.kind == IDENTIFIER) {
                          checkSourceLevel(token.pos, Feature.PATTERN_MATCHING_IN_INSTANCEOF);
                          pattern = parsePattern(patternPos, mods, type, true);
                      } else {
                          checkNoMods(typePos, mods.flags & ~Flags.DEPRECATED);
                          if (mods.annotations.nonEmpty()) {
                              checkSourceLevel(mods.annotations.head.pos, Feature.TYPE_ANNOTATIONS);
                              List<JCAnnotation> typeAnnos
                                      = mods.annotations
                                      .map(decl -> {
                                          JCAnnotation typeAnno = F.at(decl.pos)
                                                  .TypeAnnotation(decl.annotationType,
                                                          decl.args);
                                          endPosTable.replaceTree(decl, typeAnno);
                                          return typeAnno;
                                      });
                              type = insertAnnotationsToMostInner(type, typeAnnos, false);
                          }
                          pattern = type;
                      }
                  }
                  odStack[top] = F.at(pos).TypeTest(odStack[top], pattern);
              } else {
                  topOp = token;
                  nextToken();
                  top++;
                  odStack[top] = term3();
              }
              while (top > 0 && prec(topOp.kind) >= prec(token.kind)) {
                  odStack[top - 1] = F.at(topOp.pos).Binary(optag(topOp.kind), odStack[top - 1], odStack[top]);
                  top--;
                  topOp = opStack[top];
              }
          }
          Assert.check(top == 0);
          t = odStack[0];
  
          if (t.hasTag(JCTree.Tag.PLUS)) {
              t = foldStrings(t);
          }
  
          odStackSupply.add(odStack);
          opStackSupply.add(opStack);
          return t;
      }
  
      //where
  
      /**
       * If tree is a concatenation of string literals, replace it by a single
       * literal representing the concatenated string.
       */
      protected JCExpression foldStrings(JCExpression tree) {
          if (!allowStringFolding) {
              return tree;
          }
          ListBuffer<JCExpression> opStack = new ListBuffer<>();
          ListBuffer<JCLiteral> litBuf = new ListBuffer<>();
          boolean needsFolding = false;
          JCExpression curr = tree;
          while (true) {
              if (curr.hasTag(JCTree.Tag.PLUS)) {
                  JCBinary op = (JCBinary)curr;
                  needsFolding |= foldIfNeeded(op.rhs, litBuf, opStack, false);
                  curr = op.lhs;
              } else {
                  needsFolding |= foldIfNeeded(curr, litBuf, opStack, true);
                  break; //last one!
              }
          }
          if (needsFolding) {
              List<JCExpression> ops = opStack.toList();
              JCExpression res = ops.head;
              for (JCExpression op : ops.tail) {
                  res = F.at(op.getStartPosition()).Binary(optag(TokenKind.PLUS), res, op);
                  storeEnd(res, getEndPos(op));
              }
              return res;
          } else {
              return tree;
          }
      }
  
      private boolean foldIfNeeded(JCExpression tree, ListBuffer<JCLiteral> litBuf,
                                   ListBuffer<JCExpression> opStack, boolean last) {
          JCLiteral str = stringLiteral(tree);
          if (str != null) {
              litBuf.prepend(str);
              return last && merge(litBuf, opStack);
          } else {
              boolean res = merge(litBuf, opStack);
              litBuf.clear();
              opStack.prepend(tree);
              return res;
          }
      }
  
      boolean merge(ListBuffer<JCLiteral> litBuf, ListBuffer<JCExpression> opStack) {
          if (litBuf.isEmpty()) {
              return false;
          } else if (litBuf.size() == 1) {
              opStack.prepend(litBuf.first());
              return false;
          } else {
              JCExpression t = F.at(litBuf.first().getStartPosition()).Literal(TypeTag.CLASS,
                      litBuf.stream().map(lit -> (String)lit.getValue()).collect(Collectors.joining()));
              storeEnd(t, litBuf.last().getEndPosition(endPosTable));
              opStack.prepend(t);
              return true;
          }
      }
  
      private JCLiteral stringLiteral(JCTree tree) {
          if (tree.hasTag(LITERAL)) {
              JCLiteral lit = (JCLiteral)tree;
              if (lit.typetag == TypeTag.CLASS) {
                  return lit;
              }
          }
          return null;
      }
  
      /**
       * optimization: To save allocating a new operand/operator stack for every
       * binary operation, we use supplys.
       */
      ArrayList<JCExpression[]> odStackSupply = new ArrayList<>();
      ArrayList<Token[]> opStackSupply = new ArrayList<>();
  
      private JCExpression[] newOdStack() {
          if (odStackSupply.isEmpty()) {
              return new JCExpression[infixPrecedenceLevels + 1];
          }
          return odStackSupply.remove(odStackSupply.size() - 1);
      }
  
      private Token[] newOpStack() {
          if (opStackSupply.isEmpty()) {
              return new Token[infixPrecedenceLevels + 1];
          }
          return opStackSupply.remove(opStackSupply.size() - 1);
      }
  
      /**
       * Expression3 = PrefixOp Expression3 | "(" Expr | TypeNoParams ")"
       * Expression3 | Primary {Selector} {PostfixOp}
       * <p>
       * {@literal
       * Primary        = "(" Expression ")"
       * | Literal
       * | [TypeArguments] THIS [Arguments]
       * | [TypeArguments] SUPER SuperSuffix
       * | NEW [TypeArguments] Creator
       * | "(" Arguments ")" "->" ( Expression | Block )
       * | Ident "->" ( Expression | Block )
       * | [Annotations] Ident { "." [Annotations] Ident }
       * | Expression3 MemberReferenceSuffix
       * [ [Annotations] "[" ( "]" BracketsOpt "." CLASS | Expression "]" )
       * | Arguments
       * | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator )
       * ]
       * | BasicType BracketsOpt "." CLASS
       * }
       * <p>
       * PrefixOp = "++" | "--" | "!" | "~" | "+" | "-" PostfixOp = "++" | "--"
       * Type3 = Ident { "." Ident } [TypeArguments] {TypeSelector} BracketsOpt |
       * BasicType TypeNoParams3 = Ident { "." Ident } BracketsOpt Selector = "."
       * [TypeArguments] Ident [Arguments] | "." THIS | "." [TypeArguments] SUPER
       * SuperSuffix | "." NEW [TypeArguments] InnerCreator | "[" Expression "]"
       * TypeSelector = "." Ident [TypeArguments] SuperSuffix = Arguments | "."
       * Ident [Arguments]
       */
      protected JCExpression term3() {
          int pos = token.pos;
          JCExpression t;
          int prevmode = mode;
          List<JCExpression> typeArgs = typeArgumentsOpt(EXPR);
          if (typeArgs != null && token.pos <= endPosTable.errorEndPos) {
              // error recovery
              mode = prevmode;
              return F.at(pos).Erroneous(typeArgs);
          }
          switch (token.kind) {
              case QUES:
                  if ((mode & TYPE) != 0 && (mode & (TYPEARG | NOPARAMS)) == TYPEARG) {
                      selectTypeMode();
                      return typeArgument();
                  } else {
                      return illegal();
                  }
              case PLUSPLUS:
              case SUBSUB:
              case BANG:
              case TILDE:
              case PLUS:
              case SUB:
                  if (typeArgs == null && (mode & EXPR) != 0) {
                      TokenKind tk = token.kind;
                      nextToken();
                      selectExprMode();
                      if (tk == SUB
                              && (token.kind == INTLITERAL || token.kind == LONGLITERAL)
                              && token.radix() == 10) {
                          selectExprMode();
                          t = literal(names.hyphen, pos);
                      } else {
                          t = term3();
                          return F.at(pos).Unary(unoptag(tk), t);
                      }
                  } else {
                      return illegal();
                  }
                  break;
              case LPAREN:
                  if (typeArgs == null && (mode & EXPR) != 0) {
                      ParensResult pres = analyzeParens();
                      switch (pres) {
                          case CAST:
                              accept(LPAREN);
                              selectTypeMode();
                              int pos1 = pos;
                              List<JCExpression> targets = List.of(t = parseType());
                              while (token.kind == AMP) {
                                  checkSourceLevel(Feature.INTERSECTION_TYPES_IN_CAST);
                                  accept(AMP);
                                  targets = targets.prepend(parseType());
                              }
                              if (targets.length() > 1) {
                                  t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
                              }
                              accept(RPAREN);
                              selectExprMode();
                              JCExpression t1 = term3();
                              return F.at(pos).TypeCast(t, t1);
                          case IMPLICIT_LAMBDA:
                          case EXPLICIT_LAMBDA:
                              t = lambdaExpressionOrStatement(true, pres == ParensResult.EXPLICIT_LAMBDA, pos);
                              break;
                          default: //PARENS
                              accept(LPAREN);
                              selectExprMode();
                              t = termRest(term1Rest(term2Rest(term3(), TreeInfo.orPrec)));
                              accept(RPAREN);
                              t = toP(F.at(pos).Parens(t));
                              break;
                      }
                  } else {
                      return illegal();
                  }
                  break;
              case THIS:
                  if ((mode & EXPR) != 0) {
                      selectExprMode();
                      t = to(F.at(pos).Ident(names._this));
                      nextToken();
                      if (typeArgs == null) {
                          t = argumentsOpt(null, t);
                      } else {
                          t = arguments(typeArgs, t);
                      }
                      typeArgs = null;
                  } else {
                      return illegal();
                  }
                  break;
              case SUPER:
                  if ((mode & EXPR) != 0) {
                      selectExprMode();
                      t = to(F.at(pos).Ident(names._super));
                      t = superSuffix(typeArgs, t);
                      typeArgs = null;
                  } else {
                      return illegal();
                  }
                  break;
              case INTLITERAL:
              case LONGLITERAL:
              case FLOATLITERAL:
              case DOUBLELITERAL:
              case CHARLITERAL:
              case STRINGLITERAL:
              case TRUE:
              case FALSE:
              case NULL:
                  if (typeArgs == null && (mode & EXPR) != 0) {
                      selectExprMode();
                      t = literal(names.empty, token.pos);
                  } else {
                      return illegal();
                  }
                  break;
              case NEW:
                  if (typeArgs != null) {
                      return illegal();
                  }
                  if ((mode & EXPR) != 0) {
                      selectExprMode();
                      nextToken();
                      if (token.kind == LT) {
                          typeArgs = typeArguments(false);
                      }
                      t = creator(pos, typeArgs);
                      typeArgs = null;
                  } else {
                      return illegal();
                  }
                  break;
              case MONKEYS_AT:
                  // Only annotated cast types and method references are valid
                  List<JCAnnotation> typeAnnos = typeAnnotationsOpt();
                  if (typeAnnos.isEmpty()) {
                      // else there would be no '@'
                      throw new AssertionError("Expected type annotations, but found none!");
                  }
  
                  JCExpression expr = term3();
  
                  if ((mode & TYPE) == 0) {
                      // Type annotations on class literals no longer legal
                      switch (expr.getTag()) {
                          case REFERENCE: {
                              JCMemberReference mref = (JCMemberReference)expr;
                              mref.expr = toP(F.at(pos).AnnotatedType(typeAnnos, mref.expr));
                              t = mref;
                              break;
                          }
                          case SELECT: {
                              JCFieldAccess sel = (JCFieldAccess)expr;
  
                              if (sel.name != names._class) {
                                  return illegal();
                              } else {
                                  log.error(token.pos, Errors.NoAnnotationsOnDotClass);
                                  return expr;
                              }
                          }
                          default:
                              return illegal(typeAnnos.head.pos);
                      }
  
                  } else {
                      // Type annotations targeting a cast
                      t = insertAnnotationsToMostInner(expr, typeAnnos, false);
                  }
                  break;
              case UNDERSCORE:
              case IDENTIFIER:
              case ASSERT:
              case ENUM:
                  if (typeArgs != null) {
                      return illegal();
                  }
                  if ((mode & EXPR) != 0 && (mode & NOLAMBDA) == 0 && peekToken(ARROW)) {
                      t = lambdaExpressionOrStatement(false, false, pos);
                  } else {
                      t = toP(F.at(token.pos).Ident(ident()));
                      loop:
                      while (true) {
                          pos = token.pos;
                          final List<JCAnnotation> annos = typeAnnotationsOpt();
  
                          // need to report an error later if LBRACKET is for array
                          // index access rather than array creation level
                          if (!annos.isEmpty() && token.kind != LBRACKET && token.kind != ELLIPSIS) {
                              t = toP(F.at(pos).AnnotatedType(annos, t));
                              return syntaxError(annos.head.pos, List.<JCTree>of(t), (mode & EXPR) != 0 ? Errors.IllegalStartOfExpr : Errors.IllegalStartOfType);
                          }
  
                          switch (token.kind) {
                              case LBRACKET:
                                  nextToken();
                                  if (token.kind == RBRACKET) {
                                      nextToken();
                                      t = bracketsOpt(t);
                                      t = toP(F.at(pos).TypeArray(t));
                                      if (annos.nonEmpty()) {
                                          t = toP(F.at(pos).AnnotatedType(annos, t));
                                      }
                                      t = bracketsSuffix(t);
                                  } else {
                                      if ((mode & EXPR) != 0) {
                                          selectExprMode();
                                          JCExpression t1 = term();
                                          if (!annos.isEmpty()) {
                                              t = illegal(annos.head.pos);
                                          }
                                          t = to(F.at(pos).Indexed(t, t1));
                                      }
                                      accept(RBRACKET);
                                  }
                                  break loop;
                              case LPAREN:
                                  if ((mode & EXPR) != 0) {
                                      selectExprMode();
                                      t = arguments(typeArgs, t);
                                      if (!annos.isEmpty()) {
                                          t = illegal(annos.head.pos);
                                      }
                                      typeArgs = null;
                                  }
                                  break loop;
                              case DOT:
                                  nextToken();
                                  if (token.kind == TokenKind.IDENTIFIER && typeArgs != null) {
                                      return illegal();
                                  }
                                  int oldmode = mode;
                                  mode &= ~NOPARAMS;
                                  typeArgs = typeArgumentsOpt(EXPR);
                                  mode = oldmode;
                                  if ((mode & EXPR) != 0) {
                                      switch (token.kind) {
                                          case CLASS:
                                              if (typeArgs != null) {
                                                  return illegal();
                                              }
                                              selectExprMode();
                                              t = to(F.at(pos).Select(t, names._class));
                                              nextToken();
                                              break loop;
                                          case THIS:
                                              if (typeArgs != null) {
                                                  return illegal();
                                              }
                                              selectExprMode();
                                              t = to(F.at(pos).Select(t, names._this));
                                              nextToken();
                                              break loop;
                                          case SUPER:
                                              selectExprMode();
                                              t = to(F.at(pos).Select(t, names._super));
                                              t = superSuffix(typeArgs, t);
                                              typeArgs = null;
                                              break loop;
                                          case NEW:
                                              if (typeArgs != null) {
                                                  return illegal();
                                              }
                                              selectExprMode();
                                              int pos1 = token.pos;
                                              nextToken();
                                              if (token.kind == LT) {
                                                  typeArgs = typeArguments(false);
                                              }
                                              t = innerCreator(pos1, typeArgs, t);
                                              typeArgs = null;
                                              break loop;
                                      }
                                  }
  
                                  List<JCAnnotation> tyannos = null;
                                  if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
                                      tyannos = typeAnnotationsOpt();
                                  }
                                  // typeArgs saved for next loop iteration.
                                  t = toP(F.at(pos).Select(t, ident()));
                                  if (token.pos <= endPosTable.errorEndPos
                                          && token.kind == MONKEYS_AT) {
                                      //error recovery, case like:
                                      //int i = expr.<missing-ident>
                                      //@Deprecated
                                      if (typeArgs != null) {
                                          illegal();
                                      }
                                      return toP(t);
                                  }
                                  if (tyannos != null && tyannos.nonEmpty()) {
                                      t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
                                  }
                                  break;
                              case ELLIPSIS:
                                  if (this.permitTypeAnnotationsPushBack) {
                                      this.typeAnnotationsPushedBack = annos;
                                  } else if (annos.nonEmpty()) {
                                      // Don't return here -- error recovery attempt
                                      illegal(annos.head.pos);
                                  }
                                  break loop;
                              case LT:
                                  if ((mode & TYPE) == 0 && isUnboundMemberRef()) {
                                      //this is an unbound method reference whose qualifier
                                      //is a generic type i.e. A<S>::m
                                      int pos1 = token.pos;
                                      accept(LT);
                                      ListBuffer<JCExpression> args = new ListBuffer<>();
                                      args.append(typeArgument());
                                      while (token.kind == COMMA) {
                                          nextToken();
                                          args.append(typeArgument());
                                      }
                                      accept(GT);
                                      t = toP(F.at(pos1).TypeApply(t, args.toList()));
                                      while (token.kind == DOT) {
                                          nextToken();
                                          selectTypeMode();
                                          t = toP(F.at(token.pos).Select(t, ident()));
                                          t = typeArgumentsOpt(t);
                                      }
                                      t = bracketsOpt(t);
                                      if (token.kind != COLCOL) {
                                          //method reference expected here
                                          t = illegal();
                                      }
                                      selectExprMode();
                                      return term3Rest(t, typeArgs);
                                  }
                                  break loop;
                              default:
                                  break loop;
                          }
                      }
                  }
                  if (typeArgs != null) {
                      illegal();
                      t = arguments(typeArgs, t); //error recovery
                  }
                  t = typeArgumentsOpt(t);
                  break;
              case BYTE:
              case SHORT:
              case CHAR:
              case INT:
              case LONG:
              case FLOAT:
              case DOUBLE:
              case BOOLEAN:
                  if (typeArgs != null) {
                      illegal();
                  }
                  t = bracketsSuffix(bracketsOpt(basicType()));
                  break;
              case VOID:
                  if (typeArgs != null) {
                      illegal();
                  }
                  if ((mode & EXPR) != 0) {
                      nextToken();
                      if (token.kind == DOT) {
                          JCPrimitiveTypeTree ti = toP(F.at(pos).TypeIdent(TypeTag.VOID));
                          t = bracketsSuffix(ti);
                      } else {
                          return illegal(pos);
                      }
                  } else {
                      // Support the corner case of myMethodHandle.<void>invoke() by passing
                      // a void type (like other primitive types) to the next phase.
                      // The error will be reported in Attr.attribTypes or Attr.visitApply.
                      JCPrimitiveTypeTree ti = to(F.at(pos).TypeIdent(TypeTag.VOID));
                      nextToken();
                      return ti;
                      //return illegal();
                  }
                  break;
              case SWITCH:
                  checkSourceLevel(Feature.SWITCH_EXPRESSION);
                  allowYieldStatement = true;
                  int switchPos = token.pos;
                  nextToken();
                  JCExpression selector = parExpression();
                  accept(LBRACE);
                  ListBuffer<JCCase> cases = new ListBuffer<>();
                  while (true) {
                      pos = token.pos;
                      switch (token.kind) {
                          case CASE:
                          case DEFAULT:
                              cases.appendList(switchExpressionStatementGroup());
                              break;
                          case RBRACE:
                          case EOF:
                              JCSwitchExpression e = to(F.at(switchPos).SwitchExpression(selector,
                                      cases.toList()));
                              e.endpos = token.pos;
                              accept(RBRACE);
                              return e;
                          default:
                              nextToken(); // to ensure progress
                              syntaxError(pos, Errors.Expected3(CASE, DEFAULT, RBRACE));
                      }
                  }
              default:
                  return illegal();
          }
          return term3Rest(t, typeArgs);
      }
  
      private List<JCCase> switchExpressionStatementGroup() {
          ListBuffer<JCCase> caseExprs = new ListBuffer<>();
          int casePos = token.pos;
          ListBuffer<JCCaseLabel> pats = new ListBuffer<>();
  
          if (token.kind == DEFAULT) {
              nextToken();
              pats.append(toP(F.at(casePos).DefaultCaseLabel()));
          } else {
              accept(CASE);
              while (true) {
                  JCCaseLabel label = parseCaseLabel();
                  pats.append(label);
                  if (token.kind != COMMA) {
                      break;
                  }
                  checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
                  nextToken();
              }
              ;
          }
          List<JCStatement> stats = null;
          JCTree body = null;
          CaseTree.CaseKind kind;
          switch (token.kind) {
              case ARROW:
                  checkSourceLevel(Feature.SWITCH_RULE);
                  nextToken();
                  if (token.kind == TokenKind.THROW || token.kind == TokenKind.LBRACE) {
                      stats = List.of(parseStatement());
                      body = stats.head;
                      kind = JCCase.RULE;
                  } else {
                      JCExpression value = parseExpression();
                      stats = List.of(to(F.at(value).Yield(value)));
                      body = value;
                      kind = JCCase.RULE;
                      accept(SEMI);
                  }
                  break;
              default:
                  accept(COLON, tk -> Errors.Expected2(COLON, ARROW));
                  stats = blockStatements();
                  kind = JCCase.STATEMENT;
                  break;
          }
          caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), stats, body)));
          return caseExprs.toList();
      }
  
      JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) {
          if (typeArgs != null) {
              illegal();
          }
          while (true) {
              int pos1 = token.pos;
              final List<JCAnnotation> annos = typeAnnotationsOpt();
  
              if (token.kind == LBRACKET) {
                  nextToken();
                  if ((mode & TYPE) != 0) {
                      int oldmode = mode;
                      selectTypeMode();
                      if (token.kind == RBRACKET) {
                          nextToken();
                          t = bracketsOpt(t);
                          t = toP(F.at(pos1).TypeArray(t));
                          if (token.kind == COLCOL) {
                              selectExprMode();
                              continue;
                          }
                          if (annos.nonEmpty()) {
                              t = toP(F.at(pos1).AnnotatedType(annos, t));
                          }
                          return t;
                      }
                      mode = oldmode;
                  }
                  if ((mode & EXPR) != 0) {
                      selectExprMode();
                      JCExpression t1 = term();
                      t = to(F.at(pos1).Indexed(t, t1));
                  }
                  accept(RBRACKET);
              } else if (token.kind == DOT) {
                  nextToken();
                  typeArgs = typeArgumentsOpt(EXPR);
                  if (token.kind == SUPER && (mode & EXPR) != 0) {
                      selectExprMode();
                      t = to(F.at(pos1).Select(t, names._super));
                      nextToken();
                      t = arguments(typeArgs, t);
                      typeArgs = null;
                  } else if (token.kind == NEW && (mode & EXPR) != 0) {
                      if (typeArgs != null) {
                          return illegal();
                      }
                      selectExprMode();
                      int pos2 = token.pos;
                      nextToken();
                      if (token.kind == LT) {
                          typeArgs = typeArguments(false);
                      }
                      t = innerCreator(pos2, typeArgs, t);
                      typeArgs = null;
                  } else {
                      List<JCAnnotation> tyannos = null;
                      if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
                          // is the mode check needed?
                          tyannos = typeAnnotationsOpt();
                      }
                      t = toP(F.at(pos1).Select(t, ident(true)));
                      if (token.pos <= endPosTable.errorEndPos
                              && token.kind == MONKEYS_AT) {
                          //error recovery, case like:
                          //int i = expr.<missing-ident>
                          //@Deprecated
                          break;
                      }
                      if (tyannos != null && tyannos.nonEmpty()) {
                          t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
                      }
                      t = argumentsOpt(typeArgs, typeArgumentsOpt(t));
                      typeArgs = null;
                  }
              } else if ((mode & EXPR) != 0 && token.kind == COLCOL) {
                  selectExprMode();
                  if (typeArgs != null) {
                      return illegal();
                  }
                  accept(COLCOL);
                  t = memberReferenceSuffix(pos1, t);
              } else {
                  if (!annos.isEmpty()) {
                      if (permitTypeAnnotationsPushBack) {
                          typeAnnotationsPushedBack = annos;
                      } else {
                          return illegal(annos.head.pos);
                      }
                  }
                  break;
              }
          }
          while ((token.kind == PLUSPLUS || token.kind == SUBSUB) && (mode & EXPR) != 0) {
              selectExprMode();
              t = to(F.at(token.pos).Unary(
                      token.kind == PLUSPLUS ? POSTINC : POSTDEC, t));
              nextToken();
          }
          return toP(t);
      }
  
      /**
       * If we see an identifier followed by a '&lt;' it could be an unbound
       * method reference or a binary expression. To disambiguate, look for a
       * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
       */
      @SuppressWarnings("fallthrough")
      boolean isUnboundMemberRef() {
          int pos = 0, depth = 0;
          outer:
          for (Token t = S.token(pos); ; t = S.token(++pos)) {
              switch (t.kind) {
                  case IDENTIFIER:
                  case UNDERSCORE:
                  case QUES:
                  case EXTENDS:
                  case SUPER:
                  case DOT:
                  case RBRACKET:
                  case LBRACKET:
                  case COMMA:
                  case BYTE:
                  case SHORT:
                  case INT:
                  case LONG:
                  case FLOAT:
                  case DOUBLE:
                  case BOOLEAN:
                  case CHAR:
                  case MONKEYS_AT:
                      break;
  
                  case LPAREN:
                      // skip annotation values
                      int nesting = 0;
                      for (; ; pos++) {
                          TokenKind tk2 = S.token(pos).kind;
                          switch (tk2) {
                              case EOF:
                                  return false;
                              case LPAREN:
                                  nesting++;
                                  break;
                              case RPAREN:
                                  nesting--;
                                  if (nesting == 0) {
                                      continue outer;
                                  }
                                  break;
                          }
                      }
  
                  case LT:
                      depth++;
                      break;
                  case GTGTGT:
                      depth--;
                  case GTGT:
                      depth--;
                  case GT:
                      depth--;
                      if (depth == 0) {
                          TokenKind nextKind = S.token(pos + 1).kind;
                          return nextKind == TokenKind.DOT
                                  || nextKind == TokenKind.LBRACKET
                                  || nextKind == TokenKind.COLCOL;
                      }
                      break;
                  default:
                      return false;
              }
          }
      }
  
      /**
       * If we see an identifier followed by a '&lt;' it could be an unbound
       * method reference or a binary expression. To disambiguate, look for a
       * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
       */
      @SuppressWarnings("fallthrough")
      ParensResult analyzeParens() {
          int depth = 0;
          boolean type = false;
          ParensResult defaultResult = ParensResult.PARENS;
          outer:
          for (int lookahead = 0; ; lookahead++) {
              TokenKind tk = S.token(lookahead).kind;
              switch (tk) {
                  case COMMA:
                      type = true;
                  case EXTENDS:
                  case SUPER:
                  case DOT:
                  case AMP:
                      //skip
                      break;
                  case QUES:
                      if (peekToken(lookahead, EXTENDS)
                              || peekToken(lookahead, SUPER)) {
                          //wildcards
                          type = true;
                      }
                      break;
                  case BYTE:
                  case SHORT:
                  case INT:
                  case LONG:
                  case FLOAT:
                  case DOUBLE:
                  case BOOLEAN:
                  case CHAR:
                  case VOID:
                      if (peekToken(lookahead, RPAREN)) {
                          //Type, ')' -> cast
                          return ParensResult.CAST;
                      } else if (peekToken(lookahead, LAX_IDENTIFIER)) {
                          //Type, Identifier/'_'/'assert'/'enum' -> explicit lambda
                          return ParensResult.EXPLICIT_LAMBDA;
                      }
                      break;
                  case LPAREN:
                      if (lookahead != 0) {
                          // '(' in a non-starting position -> parens
                          return ParensResult.PARENS;
                      } else if (peekToken(lookahead, RPAREN, ARROW)) {
                          // '(', ')', '->' -> explicit lambda
                          return ParensResult.EXPLICIT_LAMBDA;
                      }
                      break;
                  case RPAREN:
                      // if we have seen something that looks like a type,
                      // then it's a cast expression
                      if (type) {
                          return ParensResult.CAST;
                      }
                      // otherwise, disambiguate cast vs. parenthesized expression
                      // based on subsequent token.
                      switch (S.token(lookahead + 1).kind) {
                          /*case PLUSPLUS: case SUBSUB: */
                          case BANG:
                          case TILDE:
                          case LPAREN:
                          case THIS:
                          case SUPER:
                          case INTLITERAL:
                          case LONGLITERAL:
                          case FLOATLITERAL:
                          case DOUBLELITERAL:
                          case CHARLITERAL:
                          case STRINGLITERAL:
                          case TRUE:
                          case FALSE:
                          case NULL:
                          case NEW:
                          case IDENTIFIER:
                          case ASSERT:
                          case ENUM:
                          case UNDERSCORE:
                          case SWITCH:
                          case BYTE:
                          case SHORT:
                          case CHAR:
                          case INT:
                          case LONG:
                          case FLOAT:
                          case DOUBLE:
                          case BOOLEAN:
                          case VOID:
                              return ParensResult.CAST;
                          default:
                              return defaultResult;
                      }
                  case UNDERSCORE:
                  case ASSERT:
                  case ENUM:
                  case IDENTIFIER:
                      if (peekToken(lookahead, LAX_IDENTIFIER)) {
                          // Identifier, Identifier/'_'/'assert'/'enum' -> explicit lambda
                          return ParensResult.EXPLICIT_LAMBDA;
                      } else if (peekToken(lookahead, RPAREN, ARROW)) {
                          // Identifier, ')' '->' -> implicit lambda
                          return (mode & NOLAMBDA) == 0 ? ParensResult.IMPLICIT_LAMBDA
                                  : ParensResult.PARENS;
                      } else if (depth == 0 && peekToken(lookahead, COMMA)) {
                          defaultResult = ParensResult.IMPLICIT_LAMBDA;
                      }
                      type = false;
                      break;
                  case FINAL:
                  case ELLIPSIS:
                      //those can only appear in explicit lambdas
                      return ParensResult.EXPLICIT_LAMBDA;
                  case MONKEYS_AT:
                      type = true;
                      lookahead = skipAnnotation(lookahead);
                      break;
                  case LBRACKET:
                      if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) {
                          // '[', ']', Identifier/'_'/'assert'/'enum' -> explicit lambda
                          return ParensResult.EXPLICIT_LAMBDA;
                      } else if (peekToken(lookahead, RBRACKET, RPAREN)
                              || peekToken(lookahead, RBRACKET, AMP)) {
                          // '[', ']', ')' -> cast
                          // '[', ']', '&' -> cast (intersection type)
                          return ParensResult.CAST;
                      } else if (peekToken(lookahead, RBRACKET)) {
                          //consume the ']' and skip
                          type = true;
                          lookahead++;
                          break;
                      } else {
                          return ParensResult.PARENS;
                      }
                  case LT:
                      depth++;
                      break;
                  case GTGTGT:
                      depth--;
                  case GTGT:
                      depth--;
                  case GT:
                      depth--;
                      if (depth == 0) {
                          if (peekToken(lookahead, RPAREN)
                                  || peekToken(lookahead, AMP)) {
                              // '>', ')' -> cast
                              // '>', '&' -> cast
                              return ParensResult.CAST;
                          } else if (peekToken(lookahead, LAX_IDENTIFIER, COMMA)
                                  || peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW)
                                  || peekToken(lookahead, ELLIPSIS)) {
                              // '>', Identifier/'_'/'assert'/'enum', ',' -> explicit lambda
                              // '>', Identifier/'_'/'assert'/'enum', ')', '->' -> explicit lambda
                              // '>', '...' -> explicit lambda
                              return ParensResult.EXPLICIT_LAMBDA;
                          }
                          //it looks a type, but could still be (i) a cast to generic type,
                          //(ii) an unbound method reference or (iii) an explicit lambda
                          type = true;
                          break;
                      } else if (depth < 0) {
                          //unbalanced '<', '>' - not a generic type
                          return ParensResult.PARENS;
                      }
                      break;
                  default:
                      //this includes EOF
                      return defaultResult;
              }
          }
      }
  
      private int skipAnnotation(int lookahead) {
          lookahead += 1; //skip '@'
          while (peekToken(lookahead, DOT)) {
              lookahead += 2;
          }
          if (peekToken(lookahead, LPAREN)) {
              lookahead++;
              //skip annotation values
              int nesting = 0;
              for (; ; lookahead++) {
                  TokenKind tk2 = S.token(lookahead).kind;
                  switch (tk2) {
                      case EOF:
                          return lookahead;
                      case LPAREN:
                          nesting++;
                          break;
                      case RPAREN:
                          nesting--;
                          if (nesting == 0) {
                              return lookahead;
                          }
                          break;
                  }
              }
          }
          return lookahead;
      }
  
      /**
       * Accepts all identifier-like tokens
       */
      protected Predicate<TokenKind> LAX_IDENTIFIER = t -> t == IDENTIFIER || t == UNDERSCORE || t == ASSERT || t == ENUM;
  
      enum ParensResult {
          CAST,
          EXPLICIT_LAMBDA,
          IMPLICIT_LAMBDA,
          PARENS
      }
  
      JCExpression lambdaExpressionOrStatement(boolean hasParens, boolean explicitParams, int pos) {
          List<JCVariableDecl> params = explicitParams
                  ? formalParameters(true, false)
                  : implicitParameters(hasParens);
          if (explicitParams) {
              LambdaClassifier lambdaClassifier = new LambdaClassifier();
              for (JCVariableDecl param : params) {
                  Name restrictedTypeName;
                  if (param.vartype != null
                          && (restrictedTypeName = restrictedTypeName(param.vartype, false)) != null
                          && param.vartype.hasTag(TYPEARRAY)) {
                      log.error(DiagnosticFlag.SYNTAX, param.pos,
                              Feature.VAR_SYNTAX_IMPLICIT_LAMBDAS.allowedInSource(source)
                                      ? Errors.RestrictedTypeNotAllowedArray(restrictedTypeName) : Errors.RestrictedTypeNotAllowedHere(restrictedTypeName));
                  }
                  lambdaClassifier.addParameter(param);
                  if (lambdaClassifier.result() == LambdaParameterKind.ERROR) {
                      break;
                  }
              }
              if (lambdaClassifier.diagFragment != null) {
                  log.error(DiagnosticFlag.SYNTAX, pos, Errors.InvalidLambdaParameterDeclaration(lambdaClassifier.diagFragment));
              }
              for (JCVariableDecl param : params) {
                  if (param.vartype != null
                          && restrictedTypeName(param.vartype, true) != null) {
                      checkSourceLevel(param.pos, Feature.VAR_SYNTAX_IMPLICIT_LAMBDAS);
                      param.startPos = TreeInfo.getStartPos(param.vartype);
                      param.vartype = null;
                  }
              }
          }
          return lambdaExpressionOrStatementRest(params, pos);
      }
  
      enum LambdaParameterKind {
          VAR(0),
          EXPLICIT(1),
          IMPLICIT(2),
          ERROR(-1);
  
          private final int index;
  
          LambdaParameterKind(int index) {
              this.index = index;
          }
      }
  
      private static final Fragment[][] decisionTable = new Fragment[][]{
              /*              VAR                              EXPLICIT                         IMPLICIT  */
              /* VAR      */{null, VarAndExplicitNotAllowed, VarAndImplicitNotAllowed},
              /* EXPLICIT */ {VarAndExplicitNotAllowed, null, ImplicitAndExplicitNotAllowed},
              /* IMPLICIT */ {VarAndImplicitNotAllowed, ImplicitAndExplicitNotAllowed, null},};
  
      class LambdaClassifier {
  
          LambdaParameterKind kind;
          Fragment diagFragment;
          List<JCVariableDecl> params;
  
          void addParameter(JCVariableDecl param) {
              if (param.vartype != null && param.name != names.empty) {
                  if (restrictedTypeName(param.vartype, false) != null) {
                      reduce(LambdaParameterKind.VAR);
                  } else {
                      reduce(LambdaParameterKind.EXPLICIT);
                  }
              }
              if (param.vartype == null && param.name != names.empty
                      || param.vartype != null && param.name == names.empty) {
                  reduce(LambdaParameterKind.IMPLICIT);
              }
          }
  
          private void reduce(LambdaParameterKind newKind) {
              if (kind == null) {
                  kind = newKind;
              } else if (kind != newKind && kind != LambdaParameterKind.ERROR) {
                  LambdaParameterKind currentKind = kind;
                  kind = LambdaParameterKind.ERROR;
                  boolean varIndex = currentKind.index == LambdaParameterKind.VAR.index
                          || newKind.index == LambdaParameterKind.VAR.index;
                  diagFragment = Feature.VAR_SYNTAX_IMPLICIT_LAMBDAS.allowedInSource(source) || !varIndex
                          ? decisionTable[currentKind.index][newKind.index] : null;
              }
          }
  
          LambdaParameterKind result() {
              return kind;
          }
      }
  
      JCExpression lambdaExpressionOrStatementRest(List<JCVariableDecl> args, int pos) {
          checkSourceLevel(Feature.LAMBDA);
          accept(ARROW);
  
          return token.kind == LBRACE
                  ? lambdaStatement(args, pos, token.pos)
                  : lambdaExpression(args, pos);
      }
  
      JCExpression lambdaStatement(List<JCVariableDecl> args, int pos, int pos2) {
          JCBlock block = block(pos2, 0);
          return toP(F.at(pos).Lambda(args, block));
      }
  
      JCExpression lambdaExpression(List<JCVariableDecl> args, int pos) {
          JCTree expr = parseExpression();
          if (expr.hasTag(ERRONEOUS) && ((JCErroneous)expr).errs.isEmpty()
                  && S.prevToken().kind == ARROW && S.prevToken().endPos < expr.pos) {
              expr.pos = S.prevToken().endPos;
          }
          return toP(F.at(pos).Lambda(args, expr));
      }
  
      /**
       * SuperSuffix = Arguments | "." [TypeArguments] Ident [Arguments]
       */
      JCExpression superSuffix(List<JCExpression> typeArgs, JCExpression t) {
          nextToken();
          if (token.kind == LPAREN || typeArgs != null) {
              t = arguments(typeArgs, t);
          } else if (token.kind == COLCOL) {
              if (typeArgs != null) {
                  return illegal();
              }
              t = memberReferenceSuffix(t);
          } else {
              int pos = token.pos;
              accept(DOT);
              typeArgs = (token.kind == LT) ? typeArguments(false) : null;
              t = toP(F.at(pos).Select(t, ident()));
              t = argumentsOpt(typeArgs, t);
          }
          return t;
      }
  
      /**
       * BasicType = BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE | BOOLEAN
       */
      JCPrimitiveTypeTree basicType() {
          JCPrimitiveTypeTree t = to(F.at(token.pos).TypeIdent(typetag(token.kind)));
          nextToken();
          return t;
      }
  
      /**
       * ArgumentsOpt = [ Arguments ]
       */
      JCExpression argumentsOpt(List<JCExpression> typeArgs, JCExpression t) {
          if ((mode & EXPR) != 0 && token.kind == LPAREN || typeArgs != null) {
              selectExprMode();
              return arguments(typeArgs, t);
          } else {
              return t;
          }
      }
  
      /**
       * Arguments = "(" [Expression { COMMA Expression }] ")"
       */
      List<JCExpression> arguments() {
          ListBuffer<JCExpression> args = new ListBuffer<>();
          if (token.kind == LPAREN) {
              nextToken();
              if (token.kind != RPAREN) {
                  args.append(parseExpression());
                  while (token.kind == COMMA) {
                      nextToken();
                      args.append(parseExpression());
                  }
              }
              accept(RPAREN);
          } else {
              syntaxError(token.pos, Errors.Expected(LPAREN));
          }
          return args.toList();
      }
  
      JCExpression arguments(List<JCExpression> typeArgs, JCExpression t) {
          int pos = token.pos;
          List<JCExpression> args = arguments();
          JCExpression mi = F.at(pos).Apply(typeArgs, t, args);
          if (t.hasTag(IDENT) && isInvalidUnqualifiedMethodIdentifier(((JCIdent)t).pos,
                  ((JCIdent)t).name)) {
              log.error(DiagnosticFlag.SYNTAX, t, Errors.InvalidYield);
              mi = F.Erroneous(List.of(mi));
          }
          return toP(mi);
      }
  
      boolean isInvalidUnqualifiedMethodIdentifier(int pos, Name name) {
          if (name == names.yield) {
              if (allowYieldStatement) {
                  return true;
              } else {
                  log.warning(pos, Warnings.InvalidYield);
              }
          }
          return false;
      }
  
      /**
       * TypeArgumentsOpt = [ TypeArguments ]
       */
      JCExpression typeArgumentsOpt(JCExpression t) {
          if (token.kind == LT
                  && (mode & TYPE) != 0
                  && (mode & NOPARAMS) == 0) {
              selectTypeMode();
              return typeArguments(t, false);
          } else {
              return t;
          }
      }
  
      List<JCExpression> typeArgumentsOpt() {
          return typeArgumentsOpt(TYPE);
      }
  
      List<JCExpression> typeArgumentsOpt(int useMode) {
          if (token.kind == LT) {
              JCExpression iExpr = null;
              if ((mode & useMode) == 0
                      || (mode & NOPARAMS) != 0) {
                  iExpr = illegal();
              }
              mode = useMode;
              List<JCExpression> targs = typeArguments(false);
              if (iExpr != null) {
                  setErrorEndPos(token.pos);
              }
              return targs;
          }
          return null;
      }
  
      /**
       * {@literal
       * TypeArguments  = "<" TypeArgument {"," TypeArgument} ">"
       * }
       */
      List<JCExpression> typeArguments(boolean diamondAllowed) {
          if (token.kind == LT) {
              nextToken();
              if (token.kind == GT && diamondAllowed) {
                  checkSourceLevel(Feature.DIAMOND);
                  mode |= DIAMOND;
                  nextToken();
                  return List.nil();
              } else {
                  ListBuffer<JCExpression> args = new ListBuffer<>();
                  args.append(((mode & EXPR) == 0) ? typeArgument() : parseType());
                  while (token.kind == COMMA) {
                      nextToken();
                      args.append(((mode & EXPR) == 0) ? typeArgument() : parseType());
                  }
                  switch (token.kind) {
  
                      case GTGTGTEQ:
                      case GTGTEQ:
                      case GTEQ:
                      case GTGTGT:
                      case GTGT:
                          token = S.split();
                          break;
                      case GT:
                          nextToken();
                          break;
                      default:
                          args.append(syntaxError(token.pos, Errors.Expected(GT)));
                          break;
                  }
                  return args.toList();
              }
          } else {
              return List.of(syntaxError(token.pos, Errors.Expected(LT)));
          }
      }
  
      /**
       * {@literal
       * TypeArgument = Type
       * | [Annotations] "?"
       * | [Annotations] "?" EXTENDS Type {"&" Type}
       * | [Annotations] "?" SUPER Type
       * }
       */
      JCExpression typeArgument() {
          List<JCAnnotation> annotations = typeAnnotationsOpt();
          if (token.kind != QUES) {
              return parseType(false, annotations);
          }
          int pos = token.pos;
          nextToken();
          JCExpression result;
          if (token.kind == EXTENDS) {
              TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.EXTENDS));
              nextToken();
              JCExpression bound = parseType();
              result = F.at(pos).Wildcard(t, bound);
          } else if (token.kind == SUPER) {
              TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.SUPER));
              nextToken();
              JCExpression bound = parseType();
              result = F.at(pos).Wildcard(t, bound);
          } else if (LAX_IDENTIFIER.test(token.kind)) {
              //error recovery
              TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
              JCExpression wc = toP(F.at(pos).Wildcard(t, null));
              JCIdent id = toP(F.at(token.pos).Ident(ident()));
              JCErroneous err = F.at(pos).Erroneous(List.<JCTree>of(wc, id));
              reportSyntaxError(err, Errors.Expected3(GT, EXTENDS, SUPER));
              result = err;
          } else {
              TypeBoundKind t = toP(F.at(pos).TypeBoundKind(BoundKind.UNBOUND));
              result = toP(F.at(pos).Wildcard(t, null));
          }
          if (!annotations.isEmpty()) {
              result = toP(F.at(annotations.head.pos).AnnotatedType(annotations, result));
          }
          return result;
      }
  
      JCTypeApply typeArguments(JCExpression t, boolean diamondAllowed) {
          int pos = token.pos;
          List<JCExpression> args = typeArguments(diamondAllowed);
          return toP(F.at(pos).TypeApply(t, args));
      }
  
      /**
       * BracketsOpt = { [Annotations] "[" "]" }*
       *
       * <p>
       *
       * <code>annotations</code> is the list of annotations targeting the
       * expression <code>t</code>.
       */
      private JCExpression bracketsOpt(JCExpression t,
                                       List<JCAnnotation> annotations) {
          List<JCAnnotation> nextLevelAnnotations = typeAnnotationsOpt();
  
          if (token.kind == LBRACKET) {
              if (t == null) {
                  return illegal();
              } else {
                  int pos = token.pos;
                  nextToken();
                  t = bracketsOptCont(t, pos, nextLevelAnnotations);
              }
          } else if (!nextLevelAnnotations.isEmpty()) {
              this.typeAnnotationsPushedBack = nextLevelAnnotations;
              if (!permitTypeAnnotationsPushBack) {
                  return illegal(nextLevelAnnotations.head.pos);
              }
          }
  
          if (!annotations.isEmpty()) {
              t = toP(F.at(token.pos).AnnotatedType(annotations, t));
          }
          return t;
      }
  
      /**
       * BracketsOpt = [ "[" "]" { [Annotations] "[" "]"} ]
       */
      protected JCExpression bracketsOpt(JCExpression t) {
          return bracketsOpt(t, List.nil());
      }
  
      protected JCExpression bracketsOptCont(JCExpression t, int pos,
                                             List<JCAnnotation> annotations) {
          accept(RBRACKET);
          t = bracketsOpt(t);
          t = toP(F.at(pos).TypeArray(t));
          if (annotations.nonEmpty()) {
              t = toP(F.at(pos).AnnotatedType(annotations, t));
          }
          return t;
      }
  
      /**
       * BracketsSuffixExpr = "." CLASS BracketsSuffixType =
       */
      JCExpression bracketsSuffix(JCExpression t) {
          if ((mode & EXPR) != 0 && token.kind == DOT) {
              selectExprMode();
              int pos = token.pos;
              nextToken();
              accept(CLASS);
              if (token.pos == endPosTable.errorEndPos) {
                  // error recovery
                  Name name;
                  if (LAX_IDENTIFIER.test(token.kind)) {
                      name = token.name();
                      nextToken();
                  } else {
                      name = names.error;
                  }
                  t = F.at(pos).Erroneous(List.<JCTree>of(toP(F.at(pos).Select(t, name))));
              } else {
                  Tag tag = t.getTag();
                  // Type annotations are illegal on class literals. Annotated non array class literals
                  // are complained about directly in term3(), Here check for type annotations on dimensions
                  // taking care to handle some interior dimension(s) being annotated.
                  if ((tag == TYPEARRAY && TreeInfo.containsTypeAnnotation(t)) || tag == ANNOTATED_TYPE) {
                      syntaxError(token.pos, Errors.NoAnnotationsOnDotClass);
                  }
                  t = toP(F.at(pos).Select(t, names._class));
              }
          } else if ((mode & TYPE) != 0) {
              if (token.kind != COLCOL) {
                  selectTypeMode();
              }
          } else if (token.kind != COLCOL) {
              syntaxError(token.pos, Errors.DotClassExpected);
          }
          return t;
      }
  
      /**
       * MemberReferenceSuffix = "::" [TypeArguments] Ident | "::" [TypeArguments]
       * "new"
       */
      JCExpression memberReferenceSuffix(JCExpression t) {
          int pos1 = token.pos;
          accept(COLCOL);
          return memberReferenceSuffix(pos1, t);
      }
  
      JCExpression memberReferenceSuffix(int pos1, JCExpression t) {
          checkSourceLevel(Feature.METHOD_REFERENCES);
          selectExprMode();
          List<JCExpression> typeArgs = null;
          if (token.kind == LT) {
              typeArgs = typeArguments(false);
          }
          Name refName;
          ReferenceMode refMode;
          if (token.kind == NEW) {
              refMode = ReferenceMode.NEW;
              refName = names.init;
              nextToken();
          } else {
              refMode = ReferenceMode.INVOKE;
              refName = ident();
          }
          return toP(F.at(t.getStartPosition()).Reference(refMode, refName, t, typeArgs));
      }
  
      /**
       * Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest |
       * ClassCreatorRest )
       */
      JCExpression creator(int newpos, List<JCExpression> typeArgs) {
          List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
  
          switch (token.kind) {
              case BYTE:
              case SHORT:
              case CHAR:
              case INT:
              case LONG:
              case FLOAT:
              case DOUBLE:
              case BOOLEAN:
                  if (typeArgs == null) {
                      if (newAnnotations.isEmpty()) {
                          return arrayCreatorRest(newpos, basicType());
                      } else {
                          return arrayCreatorRest(newpos, toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, basicType())));
                      }
                  }
                  break;
              default:
          }
          JCExpression t = qualident(true);
  
          int oldmode = mode;
          selectTypeMode();
          boolean diamondFound = false;
          int lastTypeargsPos = -1;
          if (token.kind == LT) {
              lastTypeargsPos = token.pos;
              t = typeArguments(t, true);
              diamondFound = (mode & DIAMOND) != 0;
          }
          while (token.kind == DOT) {
              if (diamondFound) {
                  //cannot select after a diamond
                  illegal();
              }
              int pos = token.pos;
              nextToken();
              List<JCAnnotation> tyannos = typeAnnotationsOpt();
              t = toP(F.at(pos).Select(t, ident()));
  
              if (tyannos != null && tyannos.nonEmpty()) {
                  t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
              }
  
              if (token.kind == LT) {
                  lastTypeargsPos = token.pos;
                  t = typeArguments(t, true);
                  diamondFound = (mode & DIAMOND) != 0;
              }
          }
          mode = oldmode;
          if (token.kind == LBRACKET || token.kind == MONKEYS_AT) {
              // handle type annotations for non primitive arrays
              if (newAnnotations.nonEmpty()) {
                  t = insertAnnotationsToMostInner(t, newAnnotations, false);
              }
  
              JCExpression e = arrayCreatorRest(newpos, t);
              if (diamondFound) {
                  reportSyntaxError(lastTypeargsPos, Errors.CannotCreateArrayWithDiamond);
                  return toP(F.at(newpos).Erroneous(List.of(e)));
              } else if (typeArgs != null) {
                  int pos = newpos;
                  if (!typeArgs.isEmpty() && typeArgs.head.pos != Position.NOPOS) {
                      // note: this should always happen but we should
                      // not rely on this as the parser is continuously
                      // modified to improve error recovery.
                      pos = typeArgs.head.pos;
                  }
                  setErrorEndPos(S.prevToken().endPos);
                  JCErroneous err = F.at(pos).Erroneous(typeArgs.prepend(e));
                  reportSyntaxError(err, Errors.CannotCreateArrayWithTypeArguments);
                  return toP(err);
              }
              return e;
          } else if (token.kind == LPAREN) {
              // handle type annotations for instantiations and anonymous classes
              if (newAnnotations.nonEmpty()) {
                  t = insertAnnotationsToMostInner(t, newAnnotations, false);
              }
              return classCreatorRest(newpos, null, typeArgs, t);
          } else {
              setErrorEndPos(token.pos);
              reportSyntaxError(token.pos, Errors.Expected2(LPAREN, LBRACKET));
              t = toP(F.at(newpos).NewClass(null, typeArgs, t, List.nil(), null));
              return toP(F.at(newpos).Erroneous(List.<JCTree>of(t)));
          }
      }
  
      /**
       * InnerCreator = [Annotations] Ident [TypeArguments] ClassCreatorRest
       */
      JCExpression innerCreator(int newpos, List<JCExpression> typeArgs, JCExpression encl) {
          List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
  
          JCExpression t = toP(F.at(token.pos).Ident(ident()));
  
          if (newAnnotations.nonEmpty()) {
              t = toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, t));
          }
  
          if (token.kind == LT) {
              int oldmode = mode;
              t = typeArguments(t, true);
              mode = oldmode;
          }
          return classCreatorRest(newpos, encl, typeArgs, t);
      }
  
      /**
       * ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer |
       * Expression "]" {[Annotations] "[" Expression "]"} BracketsOpt )
       */
      JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) {
          List<JCAnnotation> annos = typeAnnotationsOpt();
  
          accept(LBRACKET);
          if (token.kind == RBRACKET) {
              accept(RBRACKET);
              elemtype = bracketsOpt(elemtype, annos);
              if (token.kind == LBRACE) {
                  JCNewArray na = (JCNewArray)arrayInitializer(newpos, elemtype);
                  if (annos.nonEmpty()) {
                      // when an array initializer is present then
                      // the parsed annotations should target the
                      // new array tree
                      // bracketsOpt inserts the annotation in
                      // elemtype, and it needs to be corrected
                      //
                      JCAnnotatedType annotated = (JCAnnotatedType)elemtype;
                      assert annotated.annotations == annos;
                      na.annotations = annotated.annotations;
                      na.elemtype = annotated.underlyingType;
                  }
                  return na;
              } else {
                  JCExpression t = toP(F.at(newpos).NewArray(elemtype, List.nil(), null));
                  return syntaxError(token.pos, List.of(t), Errors.ArrayDimensionMissing);
              }
          } else {
              ListBuffer<JCExpression> dims = new ListBuffer<>();
  
              // maintain array dimension type annotations
              ListBuffer<List<JCAnnotation>> dimAnnotations = new ListBuffer<>();
              dimAnnotations.append(annos);
  
              dims.append(parseExpression());
              accept(RBRACKET);
              while (token.kind == LBRACKET
                      || token.kind == MONKEYS_AT) {
                  List<JCAnnotation> maybeDimAnnos = typeAnnotationsOpt();
                  int pos = token.pos;
                  nextToken();
                  if (token.kind == RBRACKET) { // no dimension
                      elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos);
                  } else {
                      dimAnnotations.append(maybeDimAnnos);
                      dims.append(parseExpression());
                      accept(RBRACKET);
                  }
              }
  
              List<JCExpression> elems = null;
              int errpos = token.pos;
  
              if (token.kind == LBRACE) {
                  elems = arrayInitializerElements(newpos, elemtype);
              }
  
              JCNewArray na = toP(F.at(newpos).NewArray(elemtype, dims.toList(), elems));
              na.dimAnnotations = dimAnnotations.toList();
  
              if (elems != null) {
                  return syntaxError(errpos, List.of(na), Errors.IllegalArrayCreationBothDimensionAndInitialization);
              }
  
              return na;
          }
      }
  
      /**
       * ClassCreatorRest = Arguments [ClassBody]
       */
      JCNewClass classCreatorRest(int newpos,
                                  JCExpression encl,
                                  List<JCExpression> typeArgs,
                                  JCExpression t) {
          List<JCExpression> args = arguments();
          JCClassDecl body = null;
          if (token.kind == LBRACE) {
              int pos = token.pos;
              List<JCTree> defs = classInterfaceOrRecordBody(names.empty, false, false);
              JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
              body = toP(F.at(pos).AnonymousClassDef(mods, defs));
          }
          return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
      }
  
      /**
       * ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}]
       * [","] "}"
       */
      JCExpression arrayInitializer(int newpos, JCExpression t) {
          List<JCExpression> elems = arrayInitializerElements(newpos, t);
          return toP(F.at(newpos).NewArray(t, List.nil(), elems));
      }
  
      List<JCExpression> arrayInitializerElements(int newpos, JCExpression t) {
          accept(LBRACE);
          ListBuffer<JCExpression> elems = new ListBuffer<>();
          if (token.kind == COMMA) {
              nextToken();
          } else if (token.kind != RBRACE) {
              elems.append(variableInitializer());
              while (token.kind == COMMA) {
                  nextToken();
                  if (token.kind == RBRACE) {
                      break;
                  }
                  elems.append(variableInitializer());
              }
              if (token.pos <= endPosTable.errorEndPos) {
                  skip(false, true, true, true);
              }
          }
          accept(RBRACE);
          return elems.toList();
      }
  
      /**
       * VariableInitializer = ArrayInitializer | Expression
       */
      public JCExpression variableInitializer() {
          return token.kind == LBRACE ? arrayInitializer(token.pos, null) : parseExpression();
      }
  
      /**
       * ParExpression = "(" Expression ")"
       */
      JCExpression parExpression() {
          int pos = token.pos;
          accept(LPAREN);
          JCExpression t = parseExpression();
          if (token.kind != RPAREN) {
              setErrorEndPos(token.pos);
              return syntaxError(S.prevToken().endPos, List.<JCTree>of(toP(F.at(pos).Parens(t))), Errors.Expected(RPAREN));
          }
          accept(RPAREN);
          return toP(F.at(pos).Parens(t));
      }
  
      /**
       * Block = "{" BlockStatements "}"
       */
      JCBlock block(int pos, long flags) {
          accept(LBRACE);
          List<JCStatement> stats = blockStatements();
          JCBlock t = F.at(pos).Block(flags, stats);
          while (token.kind == CASE || token.kind == DEFAULT) {
              syntaxError(token.pos, Errors.Orphaned(token.kind));
              switchBlockStatementGroups();
          }
          // the Block node has a field "endpos" for first char of last token, which is
          // usually but not necessarily the last char of the last token.
          t.endpos = token.pos;
          accept(RBRACE);
          return toP(t);
      }
  
      public JCBlock block() {
          return block(token.pos, 0);
      }
  
      /**
       * BlockStatements = { BlockStatement } BlockStatement =
       * LocalVariableDeclarationStatement | ClassOrInterfaceOrEnumDeclaration |
       * [Ident ":"] Statement LocalVariableDeclarationStatement = { FINAL | '@'
       * Annotation } Type VariableDeclarators ";"
       */
      @SuppressWarnings("fallthrough")
      List<JCStatement> blockStatements() {
          //todo: skip to anchor on error(?)
          int lastErrPos = -1;
          ListBuffer<JCStatement> stats = new ListBuffer<>();
          while (true) {
              List<JCStatement> stat = blockStatement();
              if (stat.isEmpty()) {
                  return stats.toList();
              } else {
                  // error recovery
                  if (token.pos == lastErrPos) {
                      return stats.toList();
                  }
                  if (token.pos <= endPosTable.errorEndPos) {
                      skip(false, true, true, true);
                      lastErrPos = token.pos;
                      JCStatement last = stat.last();
                      if (last.hasTag(EXEC)) {
                          JCExpression lastExpr = ((JCExpressionStatement)last).expr;
                          if (lastExpr.hasTag(ERRONEOUS) && (((JCErroneous)lastExpr).errs == null || ((JCErroneous)lastExpr).errs.isEmpty())) {
                              storeEnd(last, S.prevToken().endPos);
                              storeEnd(lastExpr, S.prevToken().endPos);
                          }
                      }
                  }
                  stats.addAll(stat);
              }
          }
      }
  
      /*
       * Parse a Statement (JLS 14.5). As an enhancement to improve error recovery,
       * this method will also recognize variable and class declarations (which are
       * not legal for a Statement) by delegating the parsing to BlockStatement (JLS 14.2).
       * If any illegal declarations are found, they will be wrapped in an erroneous tree,
       * and an error will be produced by this method.
       */
      JCStatement parseStatementAsBlock() {
          int pos = token.pos;
          List<JCStatement> stats = blockStatement();
          if (stats.isEmpty()) {
              JCErroneous e = syntaxError(pos, Errors.IllegalStartOfStmt);
              return toP(F.at(pos).Exec(e));
          } else {
              JCStatement first = stats.head;
              Error error = null;
              switch (first.getTag()) {
                  case CLASSDEF:
                      error = Errors.ClassNotAllowed;
                      break;
                  case VARDEF:
                      error = Errors.VariableNotAllowed;
                      break;
              }
              if (error != null) {
                  log.error(DiagnosticFlag.SYNTAX, first, error);
                  List<JCBlock> blist = List.of(F.at(first.pos).Block(0, stats));
                  return toP(F.at(pos).Exec(F.at(first.pos).Erroneous(blist)));
              }
              return first;
          }
      }
  
      /**
       * This method parses a statement appearing inside a block.
       */
      @SuppressWarnings("fallthrough")
      List<JCStatement> blockStatement() {
          //todo: skip to anchor on error(?)
          Comment dc;
          int pos = token.pos;
          switch (token.kind) {
              case RBRACE:
              case CASE:
              case DEFAULT:
              case EOF:
                  return List.nil();
              case LBRACE:
              case IF:
              case FOR:
              case WHILE:
              case DO:
              case TRY:
              case SWITCH:
              case SYNCHRONIZED:
              case RETURN:
              case THROW:
              case BREAK:
              case CONTINUE:
              case SEMI:
              case ELSE:
              case FINALLY:
              case CATCH:
              case ASSERT:
                  return List.of(parseSimpleStatement());
              case MONKEYS_AT:
              case FINAL: {
                  dc = token.comment(CommentStyle.JAVADOC);
                  JCModifiers mods = modifiersOpt();
                  if (token.kind == INTERFACE
                          || token.kind == CLASS
                          || token.kind == ENUM
                          || isRecordStart()) {
                      return List.of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
                  } else {
                      JCExpression t = parseType(true);
                      return localVariableDeclarations(mods, t);
                  }
              }
              case ABSTRACT:
              case STRICTFP: {
                  dc = token.comment(CommentStyle.JAVADOC);
                  JCModifiers mods = modifiersOpt();
                  return List.of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
              }
              case INTERFACE:
              case CLASS:
                  dc = token.comment(CommentStyle.JAVADOC);
                  return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
              case ENUM:
                  if (!allowRecords) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.LocalEnum);
                  }
                  dc = token.comment(CommentStyle.JAVADOC);
                  return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
              case IDENTIFIER:
                  if (token.name() == names.yield && allowYieldStatement) {
                      Token next = S.token(1);
                      boolean isYieldStatement;
                      switch (next.kind) {
                          case PLUS:
                          case SUB:
                          case STRINGLITERAL:
                          case CHARLITERAL:
                          case INTLITERAL:
                          case LONGLITERAL:
                          case FLOATLITERAL:
                          case DOUBLELITERAL:
                          case NULL:
                          case IDENTIFIER:
                          case TRUE:
                          case FALSE:
                          case NEW:
                          case SWITCH:
                          case THIS:
                          case SUPER:
                              isYieldStatement = true;
                              break;
                          case PLUSPLUS:
                          case SUBSUB:
                              isYieldStatement = S.token(2).kind != SEMI;
                              break;
                          case BANG:
                          case TILDE:
                              isYieldStatement = S.token(1).kind != SEMI;
                              break;
                          case LPAREN:
                              int lookahead = 2;
                              int balance = 1;
                              boolean hasComma = false;
                              Token l;
                              while ((l = S.token(lookahead)).kind != EOF && balance != 0) {
                                  switch (l.kind) {
                                      case LPAREN:
                                          balance++;
                                          break;
                                      case RPAREN:
                                          balance--;
                                          break;
                                      case COMMA:
                                          if (balance == 1) {
                                              hasComma = true;
                                          }
                                          break;
                                  }
                                  lookahead++;
                              }
                              isYieldStatement = (!hasComma && lookahead != 3) || l.kind == ARROW;
                              break;
                          case SEMI: //error recovery - this is not a valid statement:
                              isYieldStatement = true;
                              break;
                          default:
                              isYieldStatement = false;
                              break;
                      }
  
                      if (isYieldStatement) {
                          nextToken();
                          JCExpression t = term(EXPR);
                          accept(SEMI);
                          return List.of(toP(F.at(pos).Yield(t)));
                      }
  
                      //else intentional fall-through
                  } else {
                      if (isNonSealedClassStart(true)) {
                          log.error(token.pos, Errors.SealedOrNonSealedLocalClassesNotAllowed);
                          nextToken();
                          nextToken();
                          nextToken();
                          return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), token.comment(CommentStyle.JAVADOC)));
                      } else if (isSealedClassStart(true)) {
                          checkSourceLevel(Feature.SEALED_CLASSES);
                          log.error(token.pos, Errors.SealedOrNonSealedLocalClassesNotAllowed);
                          nextToken();
                          return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), token.comment(CommentStyle.JAVADOC)));
                      }
                  }
          }
          if (isRecordStart() && allowRecords) {
              dc = token.comment(CommentStyle.JAVADOC);
              return List.of(recordDeclaration(F.at(pos).Modifiers(0), dc));
          } else {
              Token prevToken = token;
              JCExpression t = term(EXPR | TYPE);
              if (token.kind == COLON && t.hasTag(IDENT)) {
                  nextToken();
                  JCStatement stat = parseStatementAsBlock();
                  return List.of(F.at(pos).Labelled(prevToken.name(), stat));
              } else if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.test(token.kind)) {
                  pos = token.pos;
                  JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
                  F.at(pos);
                  return localVariableDeclarations(mods, t);
              } else {
                  // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon
                  t = checkExprStat(t);
                  accept(SEMI);
                  JCExpressionStatement expr = toP(F.at(pos).Exec(t));
                  return List.of(expr);
              }
          }
      }
  
      //where
      private List<JCStatement> localVariableDeclarations(JCModifiers mods, JCExpression type) {
          ListBuffer<JCStatement> stats
                  = variableDeclarators(mods, type, new ListBuffer<>(), true);
          // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
          accept(SEMI);
          storeEnd(stats.last(), S.prevToken().endPos);
          return stats.toList();
      }
  
      /**
       * Statement = Block | IF ParExpression Statement [ELSE Statement] | FOR "("
       * ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement | FOR "("
       * FormalParameter : Expression ")" Statement | WHILE ParExpression
       * Statement | DO Statement WHILE ParExpression ";" | TRY Block ( Catches |
       * [Catches] FinallyPart ) | TRY "(" ResourceSpecification ";"opt ")" Block
       * [Catches] [FinallyPart] | SWITCH ParExpression "{"
       * SwitchBlockStatementGroups "}" | SYNCHRONIZED ParExpression Block |
       * RETURN [Expression] ";" | THROW Expression ";" | BREAK [Ident] ";" |
       * CONTINUE [Ident] ";" | ASSERT Expression [ ":" Expression ] ";" | ";"
       */
      public JCStatement parseSimpleStatement() {
          int pos = token.pos;
          switch (token.kind) {
              case LBRACE:
                  return block();
              case IF: {
                  nextToken();
                  JCExpression cond = parExpression();
                  JCStatement thenpart = parseStatementAsBlock();
                  JCStatement elsepart = null;
                  if (token.kind == ELSE) {
                      nextToken();
                      elsepart = parseStatementAsBlock();
                  }
                  return F.at(pos).If(cond, thenpart, elsepart);
              }
              case FOR: {
                  nextToken();
                  accept(LPAREN);
                  List<JCStatement> inits = token.kind == SEMI ? List.nil() : forInit();
                  if (inits.length() == 1
                          && inits.head.hasTag(VARDEF)
                          && ((JCVariableDecl)inits.head).init == null
                          && token.kind == COLON) {
                      JCVariableDecl var = (JCVariableDecl)inits.head;
                      accept(COLON);
                      JCExpression expr = parseExpression();
                      accept(RPAREN);
                      if (endPosTable.errorEndPos >= token.pos) //error recovery
                      {
                          storeEnd(expr, endPosTable.errorEndPos);
                      }
                      JCStatement body = parseStatementAsBlock();
                      return F.at(pos).ForeachLoop(var, expr, body);
                  } else {
                      accept(SEMI);
                      if (endPosTable.errorEndPos >= token.pos && inits.length() > 0) //error recovery
                      {
                          storeEnd(inits.last(), endPosTable.errorEndPos);
                      }
                      JCExpression cond = token.kind == SEMI ? null : parseExpression();
                      accept(SEMI);
                      if (endPosTable.errorEndPos >= token.pos) //error recovery
                      {
                          storeEnd(cond, endPosTable.errorEndPos);
                      }
                      List<JCExpressionStatement> steps = token.kind == RPAREN ? List.nil() : forUpdate();
                      accept(RPAREN);
                      if (endPosTable.errorEndPos >= token.pos && steps.length() > 0) //error recovery
                      {
                          storeEnd(steps.last(), endPosTable.errorEndPos);
                      }
                      JCStatement body = parseStatementAsBlock();
                      return F.at(pos).ForLoop(inits, cond, steps, body);
                  }
              }
              case WHILE: {
                  nextToken();
                  JCExpression cond = parExpression();
                  JCStatement body = parseStatementAsBlock();
                  return F.at(pos).WhileLoop(cond, body);
              }
              case DO: {
                  nextToken();
                  JCStatement body = parseStatementAsBlock();
                  accept(WHILE);
                  JCExpression cond = parExpression();
                  accept(SEMI);
                  JCDoWhileLoop t = toP(F.at(pos).DoLoop(body, cond));
                  return t;
              }
              case TRY: {
                  nextToken();
                  List<JCTree> resources = List.nil();
                  if (token.kind == LPAREN) {
                      nextToken();
                      resources = resources();
                      accept(RPAREN);
                  }
                  JCBlock body = block();
                  ListBuffer<JCCatch> catchers = new ListBuffer<>();
                  JCBlock finalizer = null;
                  if (token.kind == CATCH || token.kind == FINALLY) {
                      while (token.kind == CATCH) {
                          catchers.append(catchClause());
                      }
                      if (token.kind == FINALLY) {
                          nextToken();
                          finalizer = block();
                      }
                  } else {
                      if (resources.isEmpty()) {
                          log.error(DiagnosticFlag.SYNTAX, pos, Errors.TryWithoutCatchFinallyOrResourceDecls);
                      }
                  }
                  return F.at(pos).Try(resources, body, catchers.toList(), finalizer);
              }
              case SWITCH: {
                  nextToken();
                  JCExpression selector = parExpression();
                  accept(LBRACE);
                  List<JCCase> cases = switchBlockStatementGroups();
                  JCSwitch t = to(F.at(pos).Switch(selector, cases));
                  t.endpos = token.endPos;
                  accept(RBRACE);
                  return t;
              }
              case SYNCHRONIZED: {
                  nextToken();
                  JCExpression lock = parExpression();
                  JCBlock body = block();
                  return F.at(pos).Synchronized(lock, body);
              }
              case RETURN: {
                  nextToken();
                  JCExpression result = token.kind == SEMI ? null : parseExpression();
                  accept(SEMI);
                  JCReturn t = toP(F.at(pos).Return(result));
                  return t;
              }
              case THROW: {
                  nextToken();
                  JCExpression exc = parseExpression();
                  accept(SEMI);
                  JCThrow t = toP(F.at(pos).Throw(exc));
                  return t;
              }
              case BREAK: {
                  nextToken();
                  Name label = LAX_IDENTIFIER.test(token.kind) ? ident() : null;
                  accept(SEMI);
                  JCBreak t = toP(F.at(pos).Break(label));
                  return t;
              }
              case CONTINUE: {
                  nextToken();
                  Name label = LAX_IDENTIFIER.test(token.kind) ? ident() : null;
                  accept(SEMI);
                  JCContinue t = toP(F.at(pos).Continue(label));
                  return t;
              }
              case SEMI:
                  nextToken();
                  return toP(F.at(pos).Skip());
              case ELSE:
                  int elsePos = token.pos;
                  nextToken();
                  return doRecover(elsePos, BasicErrorRecoveryAction.BLOCK_STMT, Errors.ElseWithoutIf);
              case FINALLY:
                  int finallyPos = token.pos;
                  nextToken();
                  return doRecover(finallyPos, BasicErrorRecoveryAction.BLOCK_STMT, Errors.FinallyWithoutTry);
              case CATCH:
                  return doRecover(token.pos, BasicErrorRecoveryAction.CATCH_CLAUSE, Errors.CatchWithoutTry);
              case ASSERT: {
                  nextToken();
                  JCExpression assertion = parseExpression();
                  JCExpression message = null;
                  if (token.kind == COLON) {
                      nextToken();
                      message = parseExpression();
                  }
                  accept(SEMI);
                  JCAssert t = toP(F.at(pos).Assert(assertion, message));
                  return t;
              }
              default:
                  Assert.error();
                  return null;
          }
      }
  
      @Override
      public JCStatement parseStatement() {
          return parseStatementAsBlock();
      }
  
      private JCStatement doRecover(int startPos, ErrorRecoveryAction action, Error errorKey) {
          int errPos = S.errPos();
          JCTree stm = action.doRecover(this);
          S.errPos(errPos);
          return toP(F.Exec(syntaxError(startPos, List.of(stm), errorKey)));
      }
  
      /**
       * CatchClause = CATCH "(" FormalParameter ")" Block TODO: the
       * "FormalParameter" is not correct, it uses the special "catchTypes" rule
       * below.
       */
      protected JCCatch catchClause() {
          int pos = token.pos;
          accept(CATCH);
          accept(LPAREN);
          JCModifiers mods = optFinal(Flags.PARAMETER);
          List<JCExpression> catchTypes = catchTypes();
          JCExpression paramType = catchTypes.size() > 1
                  ? toP(F.at(catchTypes.head.getStartPosition()).TypeUnion(catchTypes))
                  : catchTypes.head;
          JCVariableDecl formal = variableDeclaratorId(mods, paramType);
          accept(RPAREN);
          JCBlock body = block();
          return F.at(pos).Catch(formal, body);
      }
  
      List<JCExpression> catchTypes() {
          ListBuffer<JCExpression> catchTypes = new ListBuffer<>();
          catchTypes.add(parseType());
          while (token.kind == BAR) {
              nextToken();
              // Instead of qualident this is now parseType.
              // But would that allow too much, e.g. arrays or generics?
              catchTypes.add(parseType());
          }
          return catchTypes.toList();
      }
  
      /**
       * SwitchBlockStatementGroups = { SwitchBlockStatementGroup }
       * SwitchBlockStatementGroup = SwitchLabel BlockStatements SwitchLabel =
       * CASE ConstantExpression ":" | DEFAULT ":"
       */
      List<JCCase> switchBlockStatementGroups() {
          ListBuffer<JCCase> cases = new ListBuffer<>();
          while (true) {
              int pos = token.pos;
              switch (token.kind) {
                  case CASE:
                  case DEFAULT:
                      cases.appendList(switchBlockStatementGroup());
                      break;
                  case RBRACE:
                  case EOF:
                      return cases.toList();
                  default:
                      nextToken(); // to ensure progress
                      syntaxError(pos, Errors.Expected3(CASE, DEFAULT, RBRACE));
              }
          }
      }
  
      protected List<JCCase> switchBlockStatementGroup() {
          int pos = token.pos;
          List<JCStatement> stats;
          JCCase c;
          ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
          switch (token.kind) {
              case CASE: {
                  nextToken();
                  ListBuffer<JCCaseLabel> pats = new ListBuffer<>();
                  while (true) {
                      pats.append(parseCaseLabel());
                      if (token.kind != COMMA) {
                          break;
                      }
                      nextToken();
                      checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
                  }
                  ;
                  CaseTree.CaseKind caseKind;
                  JCTree body = null;
                  if (token.kind == ARROW) {
                      checkSourceLevel(Feature.SWITCH_RULE);
                      accept(ARROW);
                      caseKind = JCCase.RULE;
                      JCStatement statement = parseStatementAsBlock();
                      if (!statement.hasTag(EXEC) && !statement.hasTag(BLOCK) && !statement.hasTag(Tag.THROW)) {
                          log.error(statement.pos(), Errors.SwitchCaseUnexpectedStatement);
                      }
                      stats = List.of(statement);
                      body = stats.head;
                  } else {
                      accept(COLON, tk -> Errors.Expected2(COLON, ARROW));
                      caseKind = JCCase.STATEMENT;
                      stats = blockStatements();
                  }
                  c = F.at(pos).Case(caseKind, pats.toList(), stats, body);
                  if (stats.isEmpty()) {
                      storeEnd(c, S.prevToken().endPos);
                  }
                  return cases.append(c).toList();
              }
              case DEFAULT: {
                  nextToken();
                  CaseTree.CaseKind caseKind;
                  JCTree body = null;
                  int patternPos = token.pos;
                  if (token.kind == ARROW) {
                      checkSourceLevel(Feature.SWITCH_RULE);
                      accept(ARROW);
                      caseKind = JCCase.RULE;
                      JCStatement statement = parseStatementAsBlock();
                      if (!statement.hasTag(EXEC) && !statement.hasTag(BLOCK) && !statement.hasTag(Tag.THROW)) {
                          log.error(statement.pos(), Errors.SwitchCaseUnexpectedStatement);
                      }
                      stats = List.of(statement);
                      body = stats.head;
                  } else {
                      accept(COLON, tk -> Errors.Expected2(COLON, ARROW));
                      caseKind = JCCase.STATEMENT;
                      stats = blockStatements();
                  }
                  JCCaseLabel defaultPattern = toP(F.at(patternPos).DefaultCaseLabel());
                  c = F.at(pos).Case(caseKind, List.of(defaultPattern), stats, body);
                  if (stats.isEmpty()) {
                      storeEnd(c, S.prevToken().endPos);
                  }
                  return cases.append(c).toList();
              }
          }
          throw new AssertionError("should not reach here");
      }
  
      private JCCaseLabel parseCaseLabel() {
          int patternPos = token.pos;
          JCCaseLabel label;
  
          if (token.kind == DEFAULT) {
              checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
              nextToken();
              label = toP(F.at(patternPos).DefaultCaseLabel());
          } else {
              int lookahead = 0;
              while (S.token(lookahead).kind == LPAREN) {
                  lookahead++;
              }
              JCModifiers mods = optFinal(0);
              boolean pattern = mods.flags != 0 || mods.annotations.nonEmpty()
                      || analyzePattern(lookahead) == PatternResult.PATTERN;
              if (pattern) {
                  checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
                  return parsePattern(patternPos, mods, null, false);
              } else {
                  return term(EXPR | NOLAMBDA);
              }
          }
  
          return label;
      }
  
      @SuppressWarnings("fallthrough")
      PatternResult analyzePattern(int lookahead) {
          int depth = 0;
          while (true) {
              TokenKind token = S.token(lookahead).kind;
              switch (token) {
                  case BYTE:
                  case SHORT:
                  case INT:
                  case LONG:
                  case FLOAT:
                  case DOUBLE:
                  case BOOLEAN:
                  case CHAR:
                  case VOID:
                  case ASSERT:
                  case ENUM:
                  case IDENTIFIER:
                  case UNDERSCORE:
                      if (depth == 0 && peekToken(lookahead, LAX_IDENTIFIER)) {
                          return PatternResult.PATTERN;
                      }
                      break;
                  case DOT:
                  case QUES:
                  case EXTENDS:
                  case SUPER:
                  case COMMA:
                      break;
                  case LT:
                      depth++;
                      break;
                  case GTGTGT:
                      depth--;
                  case GTGT:
                      depth--;
                  case GT:
                      depth--;
                      if (depth == 0) {
                          return peekToken(lookahead, LAX_IDENTIFIER) ? PatternResult.PATTERN
                                  : PatternResult.EXPRESSION;
                      } else if (depth < 0) {
                          return PatternResult.EXPRESSION;
                      }
                      break;
                  case MONKEYS_AT:
                      lookahead = skipAnnotation(lookahead);
                      break;
                  case LBRACKET:
                      if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) {
                          return PatternResult.PATTERN;
                      } else if (peekToken(lookahead, RBRACKET)) {
                          lookahead++;
                          break;
                      } else {
                          return PatternResult.EXPRESSION;
                      }
                  default:
                      return PatternResult.EXPRESSION;
              }
              lookahead++;
          }
      }
  
      private enum PatternResult {
          EXPRESSION,
          PATTERN;
      }
  
      /**
       * MoreStatementExpressions = { COMMA StatementExpression }
       */
      <T extends ListBuffer<? super JCExpressionStatement>> T moreStatementExpressions(int pos,
                                                                                       JCExpression first,
                                                                                       T stats) {
          // This Exec is a "StatementExpression"; it subsumes no terminating token
          stats.append(toP(F.at(pos).Exec(checkExprStat(first))));
          while (token.kind == COMMA) {
              nextToken();
              pos = token.pos;
              JCExpression t = parseExpression();
              // This Exec is a "StatementExpression"; it subsumes no terminating token
              stats.append(toP(F.at(pos).Exec(checkExprStat(t))));
          }
          return stats;
      }
  
      /**
       * ForInit = StatementExpression MoreStatementExpressions | { FINAL | '@'
       * Annotation } Type VariableDeclarators
       */
      List<JCStatement> forInit() {
          ListBuffer<JCStatement> stats = new ListBuffer<>();
          int pos = token.pos;
          if (token.kind == FINAL || token.kind == MONKEYS_AT) {
              return variableDeclarators(optFinal(0), parseType(true), stats, true).toList();
          } else {
              JCExpression t = term(EXPR | TYPE);
  
              if ((lastmode & TYPE) != 0 && (LAX_IDENTIFIER.test(token.kind) || token.kind == COLON)) {
  
                  return variableDeclarators(modifiersOpt(), t, stats, true).toList();
              } else if ((lastmode & TYPE) != 0 && token.kind == COLON) {
                  log.error(DiagnosticFlag.SYNTAX, pos, Errors.BadInitializer("for-loop"));
                  return List.of((JCStatement)F.at(pos).VarDef(modifiersOpt(), names.error, t, null));
              } else {
                  return moreStatementExpressions(pos, t, stats).toList();
              }
          }
      }
  
      /**
       * ForUpdate = StatementExpression MoreStatementExpressions
       */
      List<JCExpressionStatement> forUpdate() {
          return moreStatementExpressions(token.pos,
                  parseExpression(),
                  new ListBuffer<JCExpressionStatement>()).toList();
      }
  
      /**
       * AnnotationsOpt = { '@' Annotation }
       *
       * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION
       */
      protected List<JCAnnotation> annotationsOpt(Tag kind) {
          if (token.kind != MONKEYS_AT) {
              return List.nil(); // optimization
          }
          ListBuffer<JCAnnotation> buf = new ListBuffer<>();
          int prevmode = mode;
          while (token.kind == MONKEYS_AT) {
              int pos = token.pos;
              nextToken();
              buf.append(annotation(pos, kind));
          }
          lastmode = mode;
          mode = prevmode;
          List<JCAnnotation> annotations = buf.toList();
  
          return annotations;
      }
  
      List<JCAnnotation> typeAnnotationsOpt() {
          List<JCAnnotation> annotations = annotationsOpt(Tag.TYPE_ANNOTATION);
          return annotations;
      }
  
      /**
       * ModifiersOpt = { Modifier } Modifier = PUBLIC | PROTECTED | PRIVATE |
       * STATIC | ABSTRACT | FINAL | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE
       * | "@" | "@" Annotation
       */
      protected JCModifiers modifiersOpt() {
          return modifiersOpt(null);
      }
  
      protected JCModifiers modifiersOpt(JCModifiers partial) {
          long flags;
          ListBuffer<JCAnnotation> annotations = new ListBuffer<>();
          if (typeAnnotationsPushedBack.nonEmpty()) {
              annotations.appendList(typeAnnotationsPushedBack);
              typeAnnotationsPushedBack = List.nil();
          }
          int pos;
          if (partial == null) {
              flags = 0;
              pos = token.pos;
          } else {
              flags = partial.flags;
              annotations.appendList(partial.annotations);
              pos = partial.pos;
          }
          int lastPos;
          loop:
          while (true) {
              long flag;
              switch (token.kind) {
                  case PRIVATE:
                      flag = Flags.PRIVATE;
                      break;
                  case PROTECTED:
                      flag = Flags.PROTECTED;
                      break;
                  case PUBLIC:
                      flag = Flags.PUBLIC;
                      break;
                  case STATIC:
                      flag = Flags.STATIC;
                      break;
                  case TRANSIENT:
                      flag = Flags.TRANSIENT;
                      break;
                  case FINAL:
                      flag = Flags.FINAL;
                      break;
                  case ABSTRACT:
                      flag = Flags.ABSTRACT;
                      break;
                  case NATIVE:
                      flag = Flags.NATIVE;
                      break;
                  case VOLATILE:
                      flag = Flags.VOLATILE;
                      break;
                  case SYNCHRONIZED:
                      flag = Flags.SYNCHRONIZED;
                      break;
                  case STRICTFP:
                      flag = Flags.STRICTFP;
                      break;
                  case MONKEYS_AT:
                      flag = Flags.ANNOTATION;
                      break;
                  case DEFAULT:
                      checkSourceLevel(Feature.DEFAULT_METHODS);
                      flag = Flags.DEFAULT;
                      break;
                  case ERROR:
                      flag = 0;
                      nextToken();
                      break;
                  case IDENTIFIER: {
                      if (isNonSealedClassStart(false)) {
                          flag = Flags.NON_SEALED;
                          nextToken();
                          nextToken();
                          break;
                      }
                      if (isSealedClassStart(false)) {
                          checkSourceLevel(Feature.SEALED_CLASSES);
                          flag = Flags.SEALED;
                          break;
                      }
                      break loop;
                  }
                  default:
                      break loop;
              }
              if ((flags & flag) != 0) {
                  log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.RepeatedModifier);
              }
              lastPos = token.pos;
              if (token.deprecatedFlag()) {
                  flags |= Flags.DEPRECATED;
              }
              nextToken();
              if (flag == Flags.ANNOTATION) {
                  if (token.kind != INTERFACE) {
                      JCAnnotation ann = annotation(lastPos, Tag.ANNOTATION);
                      // if first modifier is an annotation, set pos to annotation's.
                      if (flags == 0 && annotations.isEmpty()) {
                          pos = ann.pos;
                      }
                      annotations.append(ann);
                      flag = 0;
                  }
              }
              flags |= flag;
          }
          switch (token.kind) {
              case ENUM:
                  flags |= Flags.ENUM;
                  break;
              case INTERFACE:
                  flags |= Flags.INTERFACE;
                  break;
              default:
                  break;
          }
  
          /* A modifiers tree with no modifier tokens or annotations
           * has no text position. */
          if ((flags & (Flags.ModifierFlags | Flags.ANNOTATION | Flags.DEFAULT)) == 0 && annotations.isEmpty()) {
              pos = Position.NOPOS;
          }
  
          JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList());
          if (pos != Position.NOPOS) {
              storeEnd(mods, S.prevToken().endPos);
          }
          return mods;
      }
  
      /**
       * Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ]
       *
       * @param pos  position of "@" token
       * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION
       */
      JCAnnotation annotation(int pos, Tag kind) {
          // accept(AT); // AT consumed by caller
          if (kind == Tag.TYPE_ANNOTATION) {
              checkSourceLevel(Feature.TYPE_ANNOTATIONS);
          }
          JCTree ident = qualident(false);
          int identEndPos = S.prevToken().endPos;
          boolean hasParens = token.kind == LPAREN;
          List<JCExpression> fieldValues = annotationFieldValuesOpt();
          JCAnnotation ann;
          if (kind == Tag.ANNOTATION) {
              ann = F.at(pos).Annotation(ident, fieldValues);
          } else if (kind == Tag.TYPE_ANNOTATION) {
              ann = F.at(pos).TypeAnnotation(ident, fieldValues);
          } else {
              throw new AssertionError("Unhandled annotation kind: " + kind);
          }
  
          storeEnd(ann, hasParens ? S.prevToken().endPos : identEndPos);
          return ann;
      }
  
      List<JCExpression> annotationFieldValuesOpt() {
          return (token.kind == LPAREN) ? annotationFieldValues() : List.nil();
      }
  
      /**
       * AnnotationFieldValues = "(" [ AnnotationFieldValue { ","
       * AnnotationFieldValue } ] ")"
       */
      List<JCExpression> annotationFieldValues() {
          accept(LPAREN);
          ListBuffer<JCExpression> buf = new ListBuffer<>();
          if (token.kind != RPAREN) {
              buf.append(annotationFieldValue());
              while (token.kind == COMMA) {
                  nextToken();
                  buf.append(annotationFieldValue());
              }
          }
          accept(RPAREN);
          return buf.toList();
      }
  
      /**
       * AnnotationFieldValue = AnnotationValue | Identifier "=" AnnotationValue
       */
      JCExpression annotationFieldValue() {
          if (LAX_IDENTIFIER.test(token.kind)) {
              selectExprMode();
              JCExpression t1 = term1();
              if (t1.hasTag(IDENT) && token.kind == EQ) {
                  int pos = token.pos;
                  accept(EQ);
                  JCExpression v = annotationValue();
                  return toP(F.at(pos).Assign(t1, v));
              } else {
                  return t1;
              }
          }
          return annotationValue();
      }
  
      /* AnnotationValue          = ConditionalExpression
       *                          | Annotation
       *                          | "{" [ AnnotationValue { "," AnnotationValue } ] [","] "}"
       */
      protected JCExpression annotationValue() {
          int pos;
          switch (token.kind) {
              case MONKEYS_AT:
                  pos = token.pos;
                  nextToken();
                  return annotation(pos, Tag.ANNOTATION);
              case LBRACE:
                  pos = token.pos;
                  accept(LBRACE);
                  ListBuffer<JCExpression> buf = new ListBuffer<>();
                  if (token.kind == COMMA) {
                      nextToken();
                  } else if (token.kind != RBRACE) {
                      buf.append(annotationValue());
                      while (token.kind == COMMA) {
                          nextToken();
                          if (token.kind == RBRACE) {
                              break;
                          }
                          buf.append(annotationValue());
                      }
                  }
                  accept(RBRACE);
                  return toP(F.at(pos).NewArray(null, List.nil(), buf.toList()));
              default:
                  selectExprMode();
                  return term1();
          }
      }
  
      /**
       * VariableDeclarators = VariableDeclarator { "," VariableDeclarator }
       */
      public <T extends ListBuffer<? super JCVariableDecl>> T variableDeclarators(JCModifiers mods,
                                                                                  JCExpression type,
                                                                                  T vdefs,
                                                                                  boolean localDecl) {
          return variableDeclaratorsRest(token.pos, mods, type, ident(), false, null, vdefs, localDecl);
      }
  
      /**
       * VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator
       * } ConstantDeclaratorsRest = ConstantDeclaratorRest { ","
       * ConstantDeclarator }
       *
       * @param reqInit Is an initializer always required?
       * @param dc      The documentation comment for the variable declarations, or
       *                null.
       */
      protected <T extends ListBuffer<? super JCVariableDecl>> T variableDeclaratorsRest(int pos,
                                                                                         JCModifiers mods,
                                                                                         JCExpression type,
                                                                                         Name name,
                                                                                         boolean reqInit,
                                                                                         Comment dc,
                                                                                         T vdefs,
                                                                                         boolean localDecl) {
          JCVariableDecl head = variableDeclaratorRest(pos, mods, type, name, reqInit, dc, localDecl, false);
          vdefs.append(head);
          while (token.kind == COMMA) {
              // All but last of multiple declarators subsume a comma
              storeEnd((JCTree)vdefs.last(), token.endPos);
              nextToken();
              vdefs.append(variableDeclarator(mods, type, reqInit, dc, localDecl));
          }
          return vdefs;
      }
  
      /**
       * VariableDeclarator = Ident VariableDeclaratorRest ConstantDeclarator =
       * Ident ConstantDeclaratorRest
       */
      JCVariableDecl variableDeclarator(JCModifiers mods, JCExpression type, boolean reqInit, Comment dc, boolean localDecl) {
          return variableDeclaratorRest(token.pos, mods, type, ident(), reqInit, dc, localDecl, true);
      }
  
      /**
       * VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer]
       * ConstantDeclaratorRest = BracketsOpt "=" VariableInitializer
       *
       * @param reqInit Is an initializer always required?
       * @param dc      The documentation comment for the variable declarations, or
       *                null.
       */
      JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression type, Name name,
                                            boolean reqInit, Comment dc, boolean localDecl, boolean compound) {
          boolean declaredUsingVar = false;
          JCExpression newType = bracketsOpt(type);
          if (newType.hasTag(ERRONEOUS)) {
              newType = type;
          }
  
          JCExpression init = null;
          if (token.kind == EQ) {
              nextToken();
              init = variableInitializer();
              if (init.hasTag(ERRONEOUS) && ((JCErroneous)init).errs.isEmpty() && S.prevToken().endPos < init.pos) {
                  init.pos = S.prevToken().endPos;
              }
          } else if (reqInit) {
              syntaxError(token.pos, Errors.Expected(EQ));
          }
          JCTree elemType = TreeInfo.innermostType(newType, true);
          int startPos = Position.NOPOS;
          if (elemType.hasTag(IDENT)) {
              Name typeName = ((JCIdent)elemType).name;
              if (restrictedTypeNameStartingAtSource(typeName, pos, !compound && localDecl) != null) {
  
                  if (typeName != names.var) {
                      reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedHere(typeName));
                  } else if (newType.hasTag(TYPEARRAY) && !compound) {
  
                      //error - 'var' and arrays
                      reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedArray(typeName));
                  } else {
                      declaredUsingVar = true;
                      if (compound) //error - 'var' in compound local var decl
                      {
                          reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedCompound(typeName));
                      }
                      startPos = TreeInfo.getStartPos(mods);
                      if (startPos == Position.NOPOS) {
                          startPos = TreeInfo.getStartPos(newType);
                      }
                      //implicit type
                      newType = null;
                  }
              }
          }
          JCVariableDecl result
                  = toP(F.at(pos).VarDef(mods, name, newType, init, declaredUsingVar));
  
          attach(result, dc);
          result.startPos = startPos;
          return result;
      }
  
      Name restrictedTypeName(JCExpression e, boolean shouldWarn) {
          switch (e.getTag()) {
              case IDENT:
                  return restrictedTypeNameStartingAtSource(((JCIdent)e).name, e.pos, shouldWarn) != null ? ((JCIdent)e).name : null;
              case TYPEARRAY:
                  return restrictedTypeName(((JCArrayTypeTree)e).elemtype, shouldWarn);
              default:
                  return null;
          }
      }
  
      Source restrictedTypeNameStartingAtSource(Name name, int pos, boolean shouldWarn) {
          if (name == names.var) {
              if (Feature.LOCAL_VARIABLE_TYPE_INFERENCE.allowedInSource(source)) {
                  return Source.JDK10;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK10));
              }
          }
          if (name == names.yield) {
              if (allowYieldStatement) {
                  return Source.JDK14;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK14));
              }
          }
          if (name == names.record) {
              if (allowRecords) {
                  return Source.JDK14;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14));
              }
          }
          if (name == names.sealed) {
              if (allowSealedTypes) {
                  return Source.JDK15;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
              }
          }
          if (name == names.permits) {
              if (allowSealedTypes) {
                  return Source.JDK15;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
              }
          }
          return null;
      }
  
      /**
       * VariableDeclaratorId = Ident BracketsOpt
       */
      JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type) {
          return variableDeclaratorId(mods, type, false, false);
      }
  
      //where
      JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean lambdaParameter, boolean recordComponent) {
          int pos = token.pos;
          Name name;
          if (lambdaParameter && token.kind == UNDERSCORE) {
              log.error(pos, Errors.UnderscoreAsIdentifierInLambda);
              name = token.name();
              nextToken();
          } else {
              if (allowThisIdent
                      || !lambdaParameter
                      || LAX_IDENTIFIER.test(token.kind)
                      || mods.flags != Flags.PARAMETER
                      || mods.annotations.nonEmpty()) {
                  JCExpression pn = qualident(false);
                  if (pn.hasTag(Tag.IDENT) && ((JCIdent)pn).name != names._this) {
                      name = ((JCIdent)pn).name;
                  } else {
                      if (allowThisIdent) {
                          if ((mods.flags & Flags.VARARGS) != 0) {
                              log.error(token.pos, Errors.VarargsAndReceiver);
                          }
                          if (token.kind == LBRACKET) {
                              log.error(token.pos, Errors.ArrayAndReceiver);
                          }
                          if (pn.hasTag(Tag.SELECT) && ((JCFieldAccess)pn).name != names._this) {
                              log.error(token.pos, Errors.WrongReceiver);
                          }
                      }
                      return toP(F.at(pos).ReceiverVarDef(mods, pn, type));
                  }
              } else {
                  /**
                   * if it is a lambda parameter and the token kind is not an
                   * identifier, and there are no modifiers or annotations, then
                   * this means that the compiler supposed the lambda to be
                   * explicit but it can contain a mix of implicit, var or
                   * explicit parameters. So we assign the error name to the
                   * parameter name instead of issuing an error and analyze the
                   * lambda parameters as a whole at a higher level.
                   */
                  name = names.empty;
              }
          }
          if ((mods.flags & Flags.VARARGS) != 0
                  && token.kind == LBRACKET) {
              log.error(token.pos, Errors.VarargsAndOldArraySyntax);
          }
  
          //if (type != null) {
          //type = bracketsOpt(type);
          //}
          if (recordComponent && token.kind == LBRACKET) {
              log.error(token.pos, Errors.RecordComponentAndOldArraySyntax);
          }
          type = bracketsOpt(type);
  
          return toP(F.at(pos).VarDef(mods, name, type, null,
                  type != null && type.hasTag(IDENT) && ((JCIdent)type).name == names.var));
  
      }
  
      /**
       * Resources = Resource { ";" Resources }
       */
      List<JCTree> resources() {
          ListBuffer<JCTree> defs = new ListBuffer<>();
          defs.append(resource());
          while (token.kind == SEMI) {
              //nb-javac: do not subsume the semicolon, so that it can be considered
              //to be a separator by the code generator
              nextToken();
              if (token.kind == RPAREN) { // Optional trailing semicolon
                  // after last resource
                  break;
              }
              defs.append(resource());
          }
          return defs.toList();
      }
  
      /**
       * Resource = VariableModifiersOpt Type VariableDeclaratorId "=" Expression
       * | Expression
       */
      protected JCTree resource() {
          int startPos = token.pos;
          if (token.kind == FINAL || token.kind == MONKEYS_AT) {
              JCModifiers mods = optFinal(Flags.FINAL);
              JCExpression t = parseType(true);
              return variableDeclaratorRest(token.pos, mods, t, ident(), true, null, true, false);
          }
          JCExpression t = term(EXPR | TYPE);
  
          if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.test(token.kind)) {
              JCModifiers mods = F.at(Position.NOPOS).Modifiers(Flags.FINAL);
  
              return variableDeclaratorRest(token.pos, mods, t, ident(), true, null, true, false);
          } else {
              checkSourceLevel(startPos, Feature.EFFECTIVELY_FINAL_VARIABLES_IN_TRY_WITH_RESOURCES);
              if (!t.hasTag(IDENT) && !t.hasTag(SELECT)) {
                  log.error(t.pos(), Errors.TryWithResourcesExprNeedsVar);
              }
  
              return t;
          }
      }
  
      /**
       * CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"]
       * {ImportDeclaration} {TypeDeclaration}
       */
      public JCTree.JCCompilationUnit parseCompilationUnit() {
          Token firstToken = token;
          JCModifiers mods = null;
          boolean consumedToplevelDoc = false;
          boolean seenImport = false;
          boolean seenPackage = false;
  
          boolean checkForPackage = true;
  
          ListBuffer<JCTree> defs = new ListBuffer<>();
          if (token.kind == MONKEYS_AT) {
              mods = modifiersOpt();
          }
  
          if (token.kind == PACKAGE) {
              int packagePos = token.pos;
              List<JCAnnotation> annotations = List.nil();
              seenPackage = true;
              if (mods != null) {
                  checkNoMods(mods.flags & ~Flags.DEPRECATED);
                  annotations = mods.annotations;
                  mods = null;
              }
              nextToken();
              JCExpression pid = qualident(false);
              accept(SEMI);
              JCPackageDecl pd = toP(F.at(packagePos).PackageDecl(annotations, pid));
              attach(pd, firstToken.comment(CommentStyle.JAVADOC));
              consumedToplevelDoc = true;
              defs.append(pd);
          }
  
          boolean checkForImports = true;
          boolean firstTypeDecl = true;
  
          while (token.kind != EOF) {
              if (token.pos <= endPosTable.errorEndPos) {
                  // error recovery
                  skip(checkForImports, false, true, false);
                  if (token.kind == EOF) {
                      break;
                  }
              }
              if (checkForPackage && token.kind == MONKEYS_AT) {
                  mods = modifiersOpt(null);
              } else if (token.kind == PACKAGE) {
                  if (checkForPackage) {
                      int packagePos = token.pos;
                      List<JCAnnotation> annotations = List.nil();
                      seenPackage = true;
                      checkForPackage = false;
                      if (mods != null) {
                          checkNoMods(mods.flags);
                          annotations = mods.annotations;
                          mods = null;
                      }
                      nextToken();
                      JCExpression pid = qualident(false);
                      accept(SEMI);
                      JCPackageDecl pd = toP(F.at(packagePos).PackageDecl(annotations, pid));
                      attach(pd, firstToken.comment(CommentStyle.JAVADOC));
                      consumedToplevelDoc = true;
                      defs.append(pd);
                  } else {
                      reportSyntaxError(token.pos, Errors.Expected3(CLASS, INTERFACE, ENUM));
                      nextToken();
                  }
              } else if (checkForImports && mods == null && token.kind == IMPORT) {
                  seenImport = true;
                  checkForPackage = false;
                  defs.append(importDeclaration());
              } else {
                  Comment docComment = token.comment(CommentStyle.JAVADOC);
                  if (docComment == null && firstTypeDecl && !seenImport && !seenPackage) {
                      docComment = firstToken.comment(CommentStyle.JAVADOC);
                      consumedToplevelDoc = true;
                  }
                  if (mods != null || token.kind != SEMI) {
                      mods = modifiersOpt(mods);
                  }
                  if (firstTypeDecl && token.kind == IDENTIFIER) {
                      int pos = token.pos;
                      ModuleKind kind = ModuleKind.STRONG;
                      if (token.name() == names.open) {
                          kind = ModuleKind.OPEN;
                          nextToken();
                      }
                      if (token.kind == IDENTIFIER && token.name() == names.module) {
                          if (mods != null) {
                              checkNoMods(mods.flags & ~Flags.DEPRECATED);
                          }
                          defs.append(moduleDecl(mods, kind, docComment, pos));
                          break;
                      } else if (kind != ModuleKind.STRONG) {
                          reportSyntaxError(token.pos, Errors.ExpectedModule);
                          defs.append(moduleDecl(mods, kind, docComment, pos));
                          break;
                      }
                  }
                  JCTree def = typeDeclaration(mods, docComment);
  
                  defs.append(def);
                  if (def instanceof JCClassDecl) {
                      checkForPackage = false;
                      checkForImports = false;
                      firstTypeDecl = false;
                  }
                  mods = null;
              }
          }
          JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(defs.toList());
          if (!consumedToplevelDoc) {
              attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
          }
          if (defs.isEmpty()) {
              storeEnd(toplevel, S.prevToken().endPos);
          }
          if (keepDocComments) {
              toplevel.docComments = docComments;
          }
          if (keepLineMap) {
              toplevel.lineMap = S.getLineMap();
          }
          this.endPosTable.setParser(null); // remove reference to parser
          toplevel.endPositions = this.endPosTable;
          return toplevel;
      }
  
      JCModuleDecl moduleDecl(JCModifiers mods, ModuleKind kind, Comment dc, int pos) {
          checkSourceLevel(Feature.MODULES);
          if (token.kind == IDENTIFIER && token.name() == names.module) {
              nextToken();
          }
          JCExpression name = qualident(false);
          stopAtModule = false;
          List<JCDirective> directives = null;
  
          accept(LBRACE);
          directives = moduleDirectiveList();
          accept(RBRACE);
          accept(EOF);
  
          JCModuleDecl result = toP(F.at(pos).ModuleDef(mods, kind, name, directives));
          attach(result, dc);
          return result;
      }
  
      List<JCDirective> moduleDirectiveList() {
          ListBuffer<JCDirective> defs = new ListBuffer<>();
          while (token.kind == IDENTIFIER) {
              int pos = token.pos;
              if (token.name() == names.requires) {
                  nextToken();
                  boolean isTransitive = false;
                  boolean isStaticPhase = false;
                  loop:
                  while (true) {
                      switch (token.kind) {
                          case IDENTIFIER:
                              if (token.name() == names.transitive && !isTransitive) {
                                  Token t1 = S.token(1);
                                  if (t1.kind == SEMI || t1.kind == DOT) {
                                      break loop;
                                  }
                                  isTransitive = true;
                                  break;
                              } else {
                                  break loop;
                              }
                          case STATIC:
                              if (isStaticPhase) {
                                  log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.RepeatedModifier);
                              }
                              isStaticPhase = true;
                              break;
                          default:
                              break loop;
                      }
                      nextToken();
                  }
                  JCExpression moduleName = qualident(false);
                  accept(SEMI);
                  defs.append(toP(F.at(pos).Requires(isTransitive, isStaticPhase, moduleName)));
              } else if (token.name() == names.exports || token.name() == names.opens) {
                  boolean exports = token.name() == names.exports;
                  nextToken();
                  JCExpression pkgName = qualident(false);
                  List<JCExpression> moduleNames = null;
                  if (token.kind == IDENTIFIER && token.name() == names.to) {
                      nextToken();
                      moduleNames = qualidentList(false);
                  }
                  accept(SEMI);
                  JCDirective d;
                  if (exports) {
                      d = F.at(pos).Exports(pkgName, moduleNames);
                  } else {
                      d = F.at(pos).Opens(pkgName, moduleNames);
                  }
                  defs.append(toP(d));
              } else if (token.name() == names.provides) {
                  nextToken();
                  JCExpression serviceName = qualident(false);
                  if (token.kind == IDENTIFIER && token.name() == names.with) {
                      nextToken();
                  } else {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.ExpectedStr("'" + names.with + "'"));
                  }
                  List<JCExpression> implNames = qualidentList(false);
                  accept(SEMI);
                  defs.append(toP(F.at(pos).Provides(serviceName, implNames)));
              } else if (token.name() == names.uses) {
                  nextToken();
                  JCExpression service = qualident(false);
                  accept(SEMI);
                  defs.append(toP(F.at(pos).Uses(service)));
              } else {
                  setErrorEndPos(pos);
                  reportSyntaxError(pos, Errors.InvalidModuleDirective);
                  break;
              }
          }
          return defs.toList();
      }
  
      /**
       * ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";"
       */
      protected JCTree importDeclaration() {
          int pos = token.pos;
          nextToken();
          boolean importStatic = false;
          if (token.kind == STATIC) {
              importStatic = true;
              nextToken();
          } else if (stopAtModule && token.kind == IDENTIFIER && (token.name() == names.module || token.name() == names.open) && !peekToken(DOT) && !peekToken(SEMI)) {
              // Error recovery
              JCExpression pid = F.at(token.pos).Ident(names.error);
              pid = F.at(token.pos).Select(pid, names.error);
              JCImport imp = F.at(pos).Import(pid, importStatic);
              storeEnd(imp, token.pos);
              reportSyntaxError(token.pos, Errors.Expected(IDENTIFIER));
              return imp;
          }
          JCExpression pid = toP(F.at(token.pos).Ident(ident()));
          do {
              if (stopAtModule && token.kind == IDENTIFIER && (token.name() == names.module || token.name() == names.open)) {
                  // Error recovery
                  if (pid.hasTag(IDENT)) {
                      pid = F.at(token.pos).Select(pid, names.error);
                  }
                  JCImport imp = F.at(pos).Import(pid, importStatic);
                  storeEnd(imp, token.pos);
                  reportSyntaxError(token.pos, Errors.Expected(DOT));
                  return imp;
              }
              int pos1 = token.pos;
              accept(DOT);
              if (token.kind == STAR) {
                  pid = to(F.at(pos1).Select(pid, names.asterisk));
                  nextToken();
                  break;
              } else if (stopAtModule && token.kind == IDENTIFIER && (token.name() == names.module || token.name() == names.open) && !peekToken(DOT) && !peekToken(SEMI)) {
                  // Error recovery
                  pid = F.at(pos1).Select(pid, names.error);
                  JCImport imp = F.at(pos).Import(pid, importStatic);
                  storeEnd(imp, token.pos);
                  reportSyntaxError(token.pos, Errors.Expected(IDENTIFIER));
                  return imp;
              } else {
                  pid = toP(F.at(pos1).Select(pid, ident()));
              }
  
              // Error recovery
              switch (token.kind) {
                  case ERROR:
                      String val = token.stringVal();
                      for (int i = 0; i < val.length() && val.charAt(i) == '.'; i++) {
                          if (i < val.length() - 1) {
                              pid = F.at(token.pos + i).Select(pid, names.error);
                              setErrorEndPos(token.pos + i + 1);
                              reportSyntaxError(token.pos + i, Errors.Expected(IDENTIFIER));
                              storeEnd(pid, token.pos + i + 1);
                          } else {
                              nextToken();
                              if (token.kind == STAR) {
                                  pid = to(F.at(pos1).Select(pid, names.asterisk));
                                  nextToken();
                                  break;
                              } else {
                                  pid = toP(F.at(pos1).Select(pid, ident()));
                              }
                          }
                      }
                      break;
                  case ELLIPSIS:
                      val = "...";
                      for (int i = 0; i < val.length() && val.charAt(i) == '.'; i++) {
                          if (i < val.length() - 1) {
                              pid = F.at(token.pos + i).Select(pid, names.error);
                              setErrorEndPos(token.pos + i + 1);
                              reportSyntaxError(token.pos + i, Errors.Expected(IDENTIFIER));
                              storeEnd(pid, token.pos + i + 1);
                          } else {
                              nextToken();
                              if (token.kind == STAR) {
                                  pid = to(F.at(pos1).Select(pid, names.asterisk));
                                  nextToken();
                                  break;
                              } else {
                                  pid = toP(F.at(pos1).Select(pid, ident()));
                              }
                          }
                      }
                      break;
                  case FLOATLITERAL:
                  case DOUBLELITERAL:
                      val = token.stringVal();
                      if (val.length() > 0 && val.charAt(0) == '.') {
                          setErrorEndPos(token.pos);
                          reportSyntaxError(S.prevToken().endPos, Errors.Expected(IDENTIFIER));
                          pid = to(F.at(token.pos).Select(pid, names.error));
                          nextToken();
                          if (token.kind == IDENTIFIER) {
                              nextToken();
                              storeEnd(pid, token.pos);
                          }
                      }
                      break;
              }
          } while (token.kind == DOT);
          accept(SEMI);
          return toP(F.at(pos).Import(pid, importStatic));
      }
  
      /**
       * TypeDeclaration = ClassOrInterfaceOrEnumDeclaration | ";"
       */
      JCTree typeDeclaration(JCModifiers mods, Comment docComment) {
          int pos = token.pos;
          if (mods == null && token.kind == SEMI) {
              nextToken();
              return toP(F.at(pos).Skip());
          } else {
              return classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(mods), docComment);
          }
      }
  
      /**
       * ClassOrInterfaceOrEnumDeclaration = ModifiersOpt (ClassDeclaration |
       * InterfaceDeclaration | EnumDeclaration)
       *
       * @param mods Any modifiers starting the class or interface declaration
       * @param dc   The documentation comment for the class, or null.
       */
      protected JCStatement classOrRecordOrInterfaceOrEnumDeclaration(JCModifiers mods, Comment dc) {
          if (token.kind == CLASS) {
              return classDeclaration(mods, dc);
          }
          if (isRecordStart()) {
              return recordDeclaration(mods, dc);
          } else if (token.kind == INTERFACE) {
              return interfaceDeclaration(mods, dc);
          } else if (token.kind == ENUM) {
              return enumDeclaration(mods, dc);
          } else {
              int pos = token.pos;
              List<JCTree> errs;
              if (token.kind == IDENTIFIER && token.name() == names.record) {
                  checkSourceLevel(Feature.RECORDS);
                  JCErroneous erroneousTree = syntaxError(token.pos, List.of(mods), Errors.RecordHeaderExpected);
                  nextToken();
                  return toP(F.Exec(erroneousTree));
              } else {
                  if (LAX_IDENTIFIER.test(token.kind)) {
                      errs = List.of(mods, toP(F.at(pos).Ident(ident())));
                      setErrorEndPos(token.pos);
                  } else {
                      errs = List.of(mods);
                  }
                  final JCErroneous erroneousTree;
                  if (parseModuleInfo) {
                      erroneousTree = syntaxError(pos, errs, Errors.ExpectedModuleOrOpen);
                  } else {
                      if (allowRecords) {
                          erroneousTree = syntaxError(pos, errs, Errors.Expected4(CLASS, INTERFACE, ENUM, "record"));
                      } else {
                          erroneousTree = syntaxError(pos, errs, Errors.Expected3(CLASS, INTERFACE, ENUM));
                      }
                  }
                  return toP(F.Exec(erroneousTree));
              }
          }
      }
  
      /**
       * ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type]
       * [IMPLEMENTS TypeList] ClassBody
       *
       * @param mods The modifiers starting the class declaration
       * @param dc   The documentation comment for the class, or null.
       */
      protected JCClassDecl classDeclaration(JCModifiers mods, Comment dc) {
          int pos = token.pos;
          accept(CLASS);
          Name name = typeName();
  
          List<JCTypeParameter> typarams = typeParametersOpt();
  
          JCExpression extending = null;
          if (token.kind == EXTENDS) {
              nextToken();
              extending = parseType();
          }
  
          List<JCExpression> implementing = List.nil();
          if (token.kind == IMPLEMENTS) {
              nextToken();
              implementing = typeList();
          }
          List<JCExpression> permitting = permitsClause(mods, "class");
          List<JCTree> defs = classInterfaceOrRecordBody(name, false, false);
          JCClassDecl result = toP(F.at(pos).ClassDef(
                  mods, name, typarams, extending, implementing, permitting, defs));
          attach(result, dc);
          return result;
      }
  
      protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) {
          int pos = token.pos;
          nextToken();
          mods.flags |= Flags.RECORD;
          Name name = typeName();
  
          List<JCTypeParameter> typarams = typeParametersOpt();
  
          List<JCVariableDecl> headerFields = formalParameters(false, true);
  
          List<JCExpression> implementing = List.nil();
          if (token.kind == IMPLEMENTS) {
              nextToken();
              implementing = typeList();
          }
          List<JCTree> defs = classInterfaceOrRecordBody(name, false, true);
          java.util.List<JCVariableDecl> fields = new ArrayList<>();
          for (JCVariableDecl field : headerFields) {
              fields.add(field);
          }
          for (JCTree def : defs) {
              if (def.hasTag(METHODDEF)) {
                  JCMethodDecl methDef = (JCMethodDecl)def;
                  if (methDef.name == names.init && methDef.params.isEmpty() && (methDef.mods.flags & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0) {
                      ListBuffer<JCVariableDecl> tmpParams = new ListBuffer<>();
                      for (JCVariableDecl param : headerFields) {
                          tmpParams.add(F.at(param)
                                  // we will get flags plus annotations from the record component
                                  .VarDef(F.Modifiers(Flags.PARAMETER | Flags.GENERATED_MEMBER | param.mods.flags & Flags.VARARGS,
                                                  param.mods.annotations),
                                          param.name, param.vartype, null));
                      }
                      methDef.params = tmpParams.toList();
                  }
              }
          }
          for (int i = fields.size() - 1; i >= 0; i--) {
              JCVariableDecl field = fields.get(i);
              defs = defs.prepend(field);
          }
          JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams, null, implementing, defs));
          attach(result, dc);
          return result;
      }
  
      Name typeName() {
          int pos = token.pos;
          Name name = ident();
          Source source = restrictedTypeNameStartingAtSource(name, pos, true);
          if (source != null) {
              reportSyntaxError(pos, Errors.RestrictedTypeNotAllowed(name, source));
          }
          return name;
      }
  
      /**
       * InterfaceDeclaration = INTERFACE Ident TypeParametersOpt [EXTENDS
       * TypeList] InterfaceBody
       *
       * @param mods The modifiers starting the interface declaration
       * @param dc   The documentation comment for the interface, or null.
       */
      protected JCClassDecl interfaceDeclaration(JCModifiers mods, Comment dc) {
          int pos = token.pos;
          accept(INTERFACE);
  
          Name name = typeName();
  
          List<JCTypeParameter> typarams = typeParametersOpt();
  
          List<JCExpression> extending = List.nil();
          if (token.kind == EXTENDS) {
              nextToken();
              extending = typeList();
          }
          List<JCExpression> permitting = permitsClause(mods, "interface");
          List<JCTree> defs;
          defs = classInterfaceOrRecordBody(name, true, false);
          JCClassDecl result = toP(F.at(pos).ClassDef(
                  mods, name, typarams, null, extending, permitting, defs));
          attach(result, dc);
          return result;
      }
  
      List<JCExpression> permitsClause(JCModifiers mods, String classOrInterface) {
          if (allowSealedTypes && token.kind == IDENTIFIER && token.name() == names.permits) {
              checkSourceLevel(Feature.SEALED_CLASSES);
              if ((mods.flags & Flags.SEALED) == 0) {
                  log.error(token.pos, Errors.InvalidPermitsClause(Fragments.ClassIsNotSealed(classOrInterface)));
              }
              nextToken();
              return qualidentList(false);
          }
          return List.nil();
      }
  
      /**
       * EnumDeclaration = ENUM Ident [IMPLEMENTS TypeList] EnumBody
       *
       * @param mods The modifiers starting the enum declaration
       * @param dc   The documentation comment for the enum, or null.
       */
      protected JCClassDecl enumDeclaration(JCModifiers mods, Comment dc) {
          int pos = token.pos;
          accept(ENUM);
  
          Name name = typeName();
  
          return enumDeclaration(mods, dc, pos, name);
      }
  
      JCClassDecl enumDeclaration(JCModifiers mods, Comment dc, int pos, Name name) {
          List<JCExpression> implementing = List.nil();
          if (token.kind == IMPLEMENTS) {
              nextToken();
              implementing = typeList();
          }
  
          List<JCTree> defs = enumBody(name);
          mods.flags |= Flags.ENUM;
          JCClassDecl result = toP(F.at(pos).
                  ClassDef(mods, name, List.nil(),
                          null, implementing, defs));
          attach(result, dc);
          return result;
      }
  
      /**
       * EnumBody = "{" { EnumeratorDeclarationList } [","] [ ";"
       * {ClassBodyDeclaration} ] "}"
       */
      List<JCTree> enumBody(Name enumName) {
          accept(LBRACE);
          if (token.pos <= endPosTable.errorEndPos) {
              // error recovery
              skip(false, true, false, false);
              if (token.kind == LBRACE) {
                  nextToken();
              }
          }
          ListBuffer<JCTree> defs = new ListBuffer<>();
          boolean wasSemi = false;
          boolean hasStructuralErrors = false;
          boolean wasError = false;
          if (token.kind == COMMA) {
              nextToken();
              if (token.kind == SEMI) {
                  wasSemi = true;
                  nextToken();
              } else if (token.kind != RBRACE) {
                  reportSyntaxError(S.prevToken().endPos,
                          Errors.Expected2(RBRACE, SEMI));
                  wasError = true;
              }
          }
  
          while (token.kind != RBRACE && token.kind != EOF) {
              if (token.kind == SEMI) {
                  accept(SEMI);
                  wasSemi = true;
                  if (token.kind == RBRACE || token.kind == EOF) {
                      break;
                  }
              }
              EnumeratorEstimate memberType = estimateEnumeratorOrMember(enumName);
              if (memberType == EnumeratorEstimate.UNKNOWN) {
                  memberType = wasSemi ? EnumeratorEstimate.MEMBER
                          : EnumeratorEstimate.ENUMERATOR;
              }
              if (memberType == EnumeratorEstimate.ENUMERATOR) {
                  wasError = false;
                  if (wasSemi && !hasStructuralErrors) {
                      reportSyntaxError(token.pos, Errors.EnumConstantNotExpected);
                      hasStructuralErrors = true;
                  }
                  defs.appendList(enumeratorDeclaration(enumName));
                  if (token.pos <= endPosTable.errorEndPos) {
                      // error recovery
                      skip(false, true, true, false);
                  } else {
                      if (token.kind != RBRACE && token.kind != SEMI && token.kind != EOF) {
                          if (token.kind == COMMA) {
                              nextToken();
                          } else {
                              setErrorEndPos(token.pos);
                              reportSyntaxError(S.prevToken().endPos,
                                      Errors.Expected3(COMMA, RBRACE, SEMI));
                              wasError = true;
                          }
                      }
                  }
              } else {
                  if (!wasSemi && !hasStructuralErrors && !wasError) {
                      reportSyntaxError(token.pos, Errors.EnumConstantExpected);
                      hasStructuralErrors = true;
                  }
                  wasError = false;
                  defs.appendList(classOrInterfaceOrRecordBodyDeclaration(enumName,
                          false, false));
                  if (token.pos <= endPosTable.errorEndPos) {
                      // error recovery
                      skip(false, true, true, false);
                  }
              }
          }
          accept(RBRACE);
          return defs.toList();
      }
  
      private EnumeratorEstimate estimateEnumeratorOrMember(Name enumName) {
          // if we are seeing a record declaration inside of an enum we want the same error message as expected for a
          // let's say an interface declaration inside an enum
          if (token.kind == TokenKind.IDENTIFIER && token.name() != enumName
                  && (!allowRecords || !isRecordStart())) {
              Token next = S.token(1);
              switch (next.kind) {
                  case LPAREN:
                  case LBRACE:
                  case COMMA:
                  case SEMI:
                      return EnumeratorEstimate.ENUMERATOR;
              }
          }
          switch (token.kind) {
              case IDENTIFIER:
              case MONKEYS_AT:
              case LT:
                  if (token.kind == IDENTIFIER) {
                      if (allowRecords && isRecordStart()) {
                          return EnumeratorEstimate.MEMBER;
                      }
                  }
                  return EnumeratorEstimate.UNKNOWN;
              default:
                  return EnumeratorEstimate.MEMBER;
          }
      }
  
      private enum EnumeratorEstimate {
          ENUMERATOR,
          MEMBER,
          UNKNOWN;
      }
  
      /**
       * EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [
       * Arguments ] [ "{" ClassBody "}" ]
       */
      List<JCTree> enumeratorDeclaration(Name enumName) {
          Comment dc = token.comment(CommentStyle.JAVADOC);
          int flags = Flags.PUBLIC | Flags.STATIC | Flags.FINAL | Flags.ENUM;
          if (token.deprecatedFlag()) {
              flags |= Flags.DEPRECATED;
          }
          int pos = token.pos;
          List<JCAnnotation> annotations = annotationsOpt(Tag.ANNOTATION);
          JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos).Modifiers(flags, annotations);
          List<JCExpression> typeArgs = typeArgumentsOpt();
          int identPos = token.pos;
          Name name = ident();
          if (name != names.error || token.kind == COMMA) {
              int createPos = token.pos;
              List<JCExpression> args = (token.kind == LPAREN)
                      ? arguments() : List.nil();
              JCClassDecl body = null;
              if (token.kind == LBRACE) {
                  JCModifiers mods1 = F.at(Position.NOPOS).Modifiers(Flags.ENUM);
                  List<JCTree> defs = classInterfaceOrRecordBody(names.empty, false, false);
                  body = toP(F.at(identPos).AnonymousClassDef(mods1, defs));
              }
              JCIdent ident = F.at(Position.NOPOS).Ident(enumName);
              JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body);
              if (args.nonEmpty() || body != null) {
                  storeEnd(create, S.prevToken().endPos);
              }
              ident = F.at(Position.NOPOS).Ident(enumName);
              JCTree result = toP(F.at(identPos).VarDef(mods, name, ident, create));
              attach(result, dc);
              return List.of(result);
          }
          // error recovery: not an EnumeratorDeclaration; let's try ClassBodyDeclaration
          mods = annotations.isEmpty() ? modifiersOpt() : modifiersOpt(F.at(pos).Modifiers(0, annotations));
          if (token.kind == CLASS
                  || token.kind == INTERFACE
                  || token.kind == ENUM) {
              return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
          } else if (token.kind == LBRACE
                  && (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0
                  && mods.annotations.isEmpty()) {
              return List.<JCTree>of(block(pos, mods.flags));
          } else {
              pos = token.pos;
              List<JCTypeParameter> typarams = typeParametersOpt();
              // Hack alert:  if there are type arguments but no Modifiers, the start
              // position will be lost unless we set the Modifiers position.  There
              // should be an AST node for type parameters (BugId 5005090).
              if (typarams.length() > 0 && mods.pos == Position.NOPOS) {
                  mods.pos = pos;
              }
              TokenKind tk = token.kind;
              name = tk.tag == Tokens.Token.Tag.NAMED ? token.name() : names.error;
              pos = token.pos;
              JCExpression type;
              boolean isVoid = tk == VOID;
              if (isVoid) {
                  type = to(F.at(pos).TypeIdent(TypeTag.VOID));
                  nextToken();
              } else {
                  type = parseType();
              }
              if (token.kind == LPAREN && type.hasTag(IDENT)) {
                  if (name != enumName) {
                      log.error(pos, "invalid.meth.decl.ret.type.req");
                      return List.of(methodDeclaratorRest(
                              pos, mods, null, name, typarams,
                              false, true, false, dc));
                  }
                  return List.of(methodDeclaratorRest(
                          pos, mods, null, names.init, typarams,
                          false, true, false, dc));
              } else {
                  pos = token.pos;
                  name = ident();
                  if (token.kind == LPAREN) {
                      return List.of(methodDeclaratorRest(
                              pos, mods, type, name, typarams,
                              false, isVoid, false, dc));
                  } else if (tk == ENUM && typarams.isEmpty() && (token.kind == LBRACE || token.kind == IMPLEMENTS)) {
                      log.error(pos, "enums.not.supported.in.source", source.name);
                      JCModifiers newMods
                              = F.at(mods.pos).Modifiers(mods.flags | Flags.ENUM, mods.annotations);
                      storeEnd(newMods, getEndPos(mods));
                      return List.of(enumDeclaration(newMods, dc, pos, name));
                  } else if (!isVoid && typarams.isEmpty()) {
                      List<JCTree> defs
                              = variableDeclaratorsRest(pos, mods, type, name, false, dc,
                              new ListBuffer<JCTree>(), false).toList();
                      accept(SEMI);
                      storeEnd(defs.last(), S.prevToken().endPos);
                      return defs;
                  } else {
                      pos = token.pos;
                      List<JCTree> err = isVoid
                              ? List.of(toP(F.at(pos).MethodDef(mods, name, type, typarams,
                              List.nil(), List.nil(), null, null)))
                              : List.nil();
                      return List.of(syntaxError(token.pos, err, Errors.Expected(LPAREN)));
                  }
              }
          }
      }
  
      /**
       * TypeList = Type {"," Type}
       */
      List<JCExpression> typeList() {
          ListBuffer<JCExpression> ts = new ListBuffer<>();
          ts.append(parseType());
          while (token.kind == COMMA) {
              nextToken();
              ts.append(parseType());
          }
          return ts.toList();
      }
  
      List<JCTree> classInterfaceOrRecordBody(Name className, boolean isInterface) {
          return classInterfaceOrRecordBody(className, isInterface, false);
      }
  
      /**
       * ClassBody = "{" {ClassBodyDeclaration} "}" InterfaceBody = "{"
       * {InterfaceBodyDeclaration} "}"
       */
      List<JCTree> classInterfaceOrRecordBody(Name className, boolean isInterface, boolean isRecord) {
          if (token.kind != LBRACE) {
              return List.of(syntaxError(token.pos, Errors.Expected(LBRACE)));
          }
          accept(LBRACE);
          if (token.pos <= endPosTable.errorEndPos) {
              // error recovery
              skip(false, true, false, false);
              if (token.kind == LBRACE) {
                  nextToken();
              } else {
                  return List.nil();
              }
          }
          ListBuffer<JCTree> defs = new ListBuffer<>();
          while (token.kind != RBRACE && token.kind != EOF) {
              defs.appendList(classOrInterfaceOrRecordBodyDeclaration(className, isInterface, isRecord));
              if (token.pos <= endPosTable.errorEndPos) {
                  // error recovery
                  if (token.kind == LBRACE && isInterface) {
                      nextToken();
                  }
                  skip(false, true, true, false);
              }
          }
          accept(RBRACE);
          return defs.toList();
      }
  
      public List<JCTree> classOrInterfaceBodyDeclaration(Name className, boolean isInterface) {
          boolean isRecord = false;
          if (token.kind == SEMI) {
              nextToken();
              return List.nil();
          } else {
              Comment dc = token.comment(CommentStyle.JAVADOC);
              int pos = token.pos;
              JCModifiers mods = modifiersOpt();
              if (token.kind == CLASS
                      || isRecordStart()
                      || token.kind == INTERFACE
                      || token.kind == ENUM) {
                  return List.of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
              } else if (token.kind == LBRACE
                      && (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0
                      && mods.annotations.isEmpty()) {
                  if (isInterface) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.InitializerNotAllowed);
                  } else if (isRecord && (mods.flags & Flags.STATIC) == 0) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.InstanceInitializerNotAllowedInRecords);
                  }
                  return List.of(block(pos, mods.flags));
              } else {
                  pos = token.pos;
                  List<JCTypeParameter> typarams = typeParametersOpt();
                  // if there are type parameters but no modifiers, save the start
                  // position of the method in the modifiers.
                  if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
                      mods.pos = pos;
                      storeEnd(mods, pos);
                  }
                  List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
  
                  if (annosAfterParams.nonEmpty()) {
                      checkSourceLevel(annosAfterParams.head.pos, Feature.ANNOTATIONS_AFTER_TYPE_PARAMS);
                      mods.annotations = mods.annotations.appendList(annosAfterParams);
                      if (mods.pos == Position.NOPOS) {
                          mods.pos = mods.annotations.head.pos;
                      }
                  }
  
                  Token tk = token;
                  Name name = token.kind.tag == Tokens.Token.Tag.NAMED ? token.name() : names.error;
                  pos = token.pos;
                  JCExpression type;
                  boolean isVoid = token.kind == VOID;
                  if (isVoid) {
                      type = to(F.at(pos).TypeIdent(TypeTag.VOID));
                      nextToken();
                  } else {
                      // method returns types are un-annotated types
                      type = unannotatedType(false);
                  }
                  if ((token.kind == LPAREN && !isInterface
                          || isRecord && token.kind == LBRACE) && type.hasTag(IDENT)) {
                      if (isInterface || name != className) {
                          log.error(DiagnosticFlag.SYNTAX, pos, Errors.InvalidMethDeclRetTypeReq);
                          return List.of(methodDeclaratorRest(
                                  pos, mods, null, name, typarams,
                                  isInterface, true, isRecord, dc));
                      } else if (annosAfterParams.nonEmpty()) {
                          illegal(annosAfterParams.head.pos);
                      }
                      if (isRecord && token.kind == LBRACE) {
                          mods.flags |= Flags.COMPACT_RECORD_CONSTRUCTOR;
                      }
                      return List.of(methodDeclaratorRest(
                              pos, mods, null, names.init, typarams,
                              isInterface, true, isRecord, dc));
                  } else {
                      pos = token.pos;
                      name = ident();
                      if (token.kind == LPAREN) {
                          return List.of(methodDeclaratorRest(
                                  pos, mods, type, name, typarams,
                                  isInterface, isVoid, false, dc));
                      } else if (token.kind == ENUM && typarams.isEmpty() && (token.kind == LBRACE || token.kind == IMPLEMENTS)) {
                          log.error(pos, "enums.not.supported.in.source", source.name);
                          JCModifiers newMods
                                  = F.at(mods.pos).Modifiers(mods.flags | Flags.ENUM, mods.annotations);
                          storeEnd(newMods, getEndPos(mods));
                          return List.of(enumDeclaration(newMods, dc, pos, name));
                      } else if (!isVoid && typarams.isEmpty()) {
                          if (!isRecord || (isRecord && (mods.flags & Flags.STATIC) != 0)) {
                              List<JCTree> defs
                                      = variableDeclaratorsRest(pos, mods, type, name, isInterface, dc,
                                      new ListBuffer<JCTree>(), false).toList();
                              if (token.kind == SEMI) {
                                  nextToken();
                                  storeEnd(defs.last(), S.prevToken().endPos);
                                  if (typeAnnotationsPushedBack.nonEmpty()) {
                                      typeAnnotationsPushedBack = List.nil();
                                  }
                              } else {
                                  if (typeAnnotationsPushedBack.nonEmpty()) {
                                      storeEnd(defs.last(), typeAnnotationsPushedBack.head.pos);
                                  } else {
                                      setErrorEndPos(token.pos);
                                      reportSyntaxError(S.prevToken().endPos, Errors.Expected(SEMI));
                                      storeEnd(defs.last(), S.prevToken().endPos);
                                  }
                              }
                              return defs;
                          } else {
                              int errPos = pos;
                              variableDeclaratorsRest(pos, mods, type, name, isInterface, dc,
                                      new ListBuffer<JCTree>(), false).toList();
                              accept(SEMI);
                              return List.of(syntaxError(errPos, null, Errors.RecordCannotDeclareInstanceFields));
                          }
                      } else {
                          pos = token.pos;
                          List<JCTree> err;
                          if (isVoid || typarams.nonEmpty()) {
                              JCMethodDecl m
                                      = toP(F.at(pos).MethodDef(mods, name, type, typarams,
                                      List.nil(), List.nil(), null, null));
                              attach(m, dc);
                              err = List.of(m);
                          } else {
                              err = List.nil();
                          }
                          return List.of(syntaxError(token.pos, err, Errors.Expected(LPAREN)));
                      }
                  }
              }
          }
      }
  
      /**
       * ClassBodyDeclaration = ";" | [STATIC] Block | ModifiersOpt ( Type Ident (
       * VariableDeclaratorsRest ";" | MethodDeclaratorRest ) | VOID Ident
       * VoidMethodDeclaratorRest | TypeParameters [Annotations] ( Type Ident
       * MethodDeclaratorRest | VOID Ident VoidMethodDeclaratorRest ) | Ident
       * ConstructorDeclaratorRest | TypeParameters Ident
       * ConstructorDeclaratorRest | ClassOrInterfaceOrEnumDeclaration )
       * InterfaceBodyDeclaration = ";" | ModifiersOpt ( Type Ident (
       * ConstantDeclaratorsRest ";" | MethodDeclaratorRest ) | VOID Ident
       * MethodDeclaratorRest | TypeParameters [Annotations] ( Type Ident
       * MethodDeclaratorRest | VOID Ident VoidMethodDeclaratorRest ) |
       * ClassOrInterfaceOrEnumDeclaration )
       */
      public List<JCTree> classOrInterfaceOrRecordBodyDeclaration(Name className, boolean isInterface, boolean isRecord) {
          if (token.kind == SEMI) {
              nextToken();
              return List.nil();
          } else {
              Comment dc = token.comment(CommentStyle.JAVADOC);
              int pos = token.pos;
              JCModifiers mods = modifiersOpt();
              if (token.kind == CLASS
                      || isRecordStart()
                      || token.kind == INTERFACE
                      || token.kind == ENUM) {
                  return List.of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
              } else if (token.kind == LBRACE
                      && (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0
                      && mods.annotations.isEmpty()) {
                  if (isInterface) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.InitializerNotAllowed);
                  } else if (isRecord && (mods.flags & Flags.STATIC) == 0) {
                      log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.InstanceInitializerNotAllowedInRecords);
                  }
                  return List.of(block(pos, mods.flags));
              } else {
                  pos = token.pos;
                  List<JCTypeParameter> typarams = typeParametersOpt();
                  // if there are type parameters but no modifiers, save the start
                  // position of the method in the modifiers.
                  if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
                      mods.pos = pos;
                      storeEnd(mods, pos);
                  }
                  List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
  
                  if (annosAfterParams.nonEmpty()) {
                      checkSourceLevel(annosAfterParams.head.pos, Feature.ANNOTATIONS_AFTER_TYPE_PARAMS);
                      mods.annotations = mods.annotations.appendList(annosAfterParams);
                      if (mods.pos == Position.NOPOS) {
                          mods.pos = mods.annotations.head.pos;
                      }
                  }
  
                  Token tk = token;
                  Name name = token.kind.tag == Tokens.Token.Tag.NAMED ? token.name() : names.error;
                  pos = token.pos;
                  JCExpression type;
                  boolean isVoid = token.kind == VOID;
                  if (isVoid) {
                      type = to(F.at(pos).TypeIdent(TypeTag.VOID));
                      nextToken();
                  } else {
                      // method returns types are un-annotated types
                      type = unannotatedType(false);
                  }
                  if ((token.kind == LPAREN && !isInterface
                          || isRecord && token.kind == LBRACE) && type.hasTag(IDENT)) {
                      if (isInterface || name != className) {
                          log.error(DiagnosticFlag.SYNTAX, pos, Errors.InvalidMethDeclRetTypeReq);
                          return List.of(methodDeclaratorRest(
                                  pos, mods, null, name, typarams,
                                  isInterface, true, isRecord, dc));
                      } else if (annosAfterParams.nonEmpty()) {
                          illegal(annosAfterParams.head.pos);
                      }
                      if (isRecord && token.kind == LBRACE) {
                          mods.flags |= Flags.COMPACT_RECORD_CONSTRUCTOR;
                      }
                      return List.of(methodDeclaratorRest(
                              pos, mods, null, names.init, typarams,
                              isInterface, true, isRecord, dc));
                  } else if (isRecord && type.hasTag(IDENT) && token.kind == THROWS) {
                      // trying to define a compact constructor with a throws clause
                      log.error(DiagnosticFlag.SYNTAX, token.pos,
                              Errors.InvalidCanonicalConstructorInRecord(
                                      Fragments.Compact,
                                      className,
                                      Fragments.ThrowsClauseNotAllowedForCanonicalConstructor(Fragments.Compact)));
                      skip(false, true, false, false);
                      return List.of(methodDeclaratorRest(
                              pos, mods, null, names.init, typarams,
                              isInterface, true, isRecord, dc));
                  } else {
                      pos = token.pos;
                      name = ident();
                      if (token.kind == LPAREN) {
                          return List.of(methodDeclaratorRest(
                                  pos, mods, type, name, typarams,
                                  isInterface, isVoid, false, dc));
                      } else if (token.kind == ENUM && typarams.isEmpty() && (token.kind == LBRACE || token.kind == IMPLEMENTS)) {
                          log.error(pos, "enums.not.supported.in.source", source.name);
                          JCModifiers newMods
                                  = F.at(mods.pos).Modifiers(mods.flags | Flags.ENUM, mods.annotations);
                          storeEnd(newMods, getEndPos(mods));
                          return List.of(enumDeclaration(newMods, dc, pos, name));
                      } else if (!isVoid && typarams.isEmpty()) {
                          if (!isRecord || (isRecord && (mods.flags & Flags.STATIC) != 0)) {
                              List<JCTree> defs
                                      = variableDeclaratorsRest(pos, mods, type, name, isInterface, dc,
                                      new ListBuffer<JCTree>(), false).toList();
                              if (token.kind == SEMI) {
                                  nextToken();
                                  storeEnd(defs.last(), S.prevToken().endPos);
                                  if (typeAnnotationsPushedBack.nonEmpty()) {
                                      typeAnnotationsPushedBack = List.nil();
                                  }
                              } else {
                                  if (typeAnnotationsPushedBack.nonEmpty()) {
                                      storeEnd(defs.last(), typeAnnotationsPushedBack.head.pos);
                                  } else {
                                      setErrorEndPos(token.pos);
                                      reportSyntaxError(S.prevToken().endPos, Errors.Expected(SEMI));
                                      storeEnd(defs.last(), S.prevToken().endPos);
                                  }
                              }
                              return defs;
                          } else {
                              int errPos = pos;
                              variableDeclaratorsRest(pos, mods, type, name, isInterface, dc,
                                      new ListBuffer<JCTree>(), false).toList();
                              accept(SEMI);
                              return List.of(syntaxError(errPos, null, Errors.RecordCannotDeclareInstanceFields));
                          }
                      } else {
                          pos = token.pos;
                          List<JCTree> err;
                          if (isVoid || typarams.nonEmpty()) {
                              JCMethodDecl m
                                      = toP(F.at(pos).MethodDef(mods, name, type, typarams,
                                      List.nil(), List.nil(), null, null));
                              attach(m, dc);
                              err = List.of(m);
                          } else {
                              err = List.nil();
                          }
                          return List.of(syntaxError(token.pos, err, Errors.Expected(LPAREN)));
                      }
                  }
              }
          }
      }
  
      public boolean isRecordStart() {
          if (token.kind == IDENTIFIER && token.name() == names.record
                  && (peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN)
                  || peekToken(TokenKind.IDENTIFIER, TokenKind.EOF)
                  || peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
              checkSourceLevel(Feature.RECORDS);
              return true;
          } else {
              return false;
          }
      }
  
      protected boolean isNonSealedClassStart(boolean local) {
          if (isNonSealedIdentifier(token, 0)) {
              Token next = S.token(3);
              return allowedAfterSealedOrNonSealed(next, local, true);
          }
          return false;
      }
  
      protected boolean isNonSealedIdentifier(Token someToken, int lookAheadOffset) {
          if (someToken.name() == names.non && peekToken(lookAheadOffset, TokenKind.SUB, TokenKind.IDENTIFIER)) {
              Token tokenSub = S.token(lookAheadOffset + 1);
              Token tokenSealed = S.token(lookAheadOffset + 2);
              if (someToken.endPos == tokenSub.pos
                      && tokenSub.endPos == tokenSealed.pos
                      && tokenSealed.name() == names.sealed) {
                  checkSourceLevel(Feature.SEALED_CLASSES);
                  return true;
              }
          }
          return false;
      }
  
      protected boolean isSealedClassStart(boolean local) {
          if (token.name() == names.sealed) {
              Token next = S.token(1);
              if (allowedAfterSealedOrNonSealed(next, local, false)) {
                  checkSourceLevel(Feature.SEALED_CLASSES);
                  return true;
              }
          }
          return false;
      }
  
      private boolean allowedAfterSealedOrNonSealed(Token next, boolean local, boolean currentIsNonSealed) {
          boolean isAllowed;
          if (local) {
              switch (next.kind) {
  
                  case MONKEYS_AT: {
                      Token afterNext = S.token(2);
                      return afterNext.kind != INTERFACE || currentIsNonSealed;
                  }
                  case ABSTRACT:
                  case FINAL:
                  case STRICTFP:
                  case CLASS:
                  case INTERFACE:
                  case ENUM:
                      isAllowed = true;
                      break;
                  default:
                      isAllowed = false;
              }
          } else {
              switch (next.kind) {
                  case MONKEYS_AT:
                      Token afterNext = S.token(2);
                      return afterNext.kind != INTERFACE || currentIsNonSealed;
  
                  case PUBLIC:
                  case PROTECTED:
                  case PRIVATE:
                  case ABSTRACT:
                  case STATIC:
                  case FINAL:
                  case STRICTFP:
                  case CLASS:
                  case INTERFACE:
                  case ENUM:
                      isAllowed = true;
                      break;
                  case IDENTIFIER:
                      isAllowed = isNonSealedIdentifier(next, currentIsNonSealed ? 3 : 1) || next.name() == names.sealed;
                      break;
                  default:
                      isAllowed = false;
              }
          }
          return isAllowed;
      }
  
      /**
       * MethodDeclaratorRest = FormalParameters BracketsOpt [THROWS TypeList] (
       * MethodBody | [DEFAULT AnnotationValue] ";") VoidMethodDeclaratorRest =
       * FormalParameters [THROWS TypeList] ( MethodBody | ";")
       * ConstructorDeclaratorRest = "(" FormalParameterListOpt ")" [THROWS
       * TypeList] MethodBody
       */
      protected JCTree methodDeclaratorRest(int pos,
                                            JCModifiers mods,
                                            JCExpression type,
                                            Name name,
                                            List<JCTypeParameter> typarams,
                                            boolean isInterface, boolean isVoid,
                                            boolean isRecord,
                                            Comment dc) {
          if (isInterface) {
              if ((mods.flags & Flags.STATIC) != 0) {
                  checkSourceLevel(Feature.STATIC_INTERFACE_METHODS);
              }
              if ((mods.flags & Flags.PRIVATE) != 0) {
                  checkSourceLevel(Feature.PRIVATE_INTERFACE_METHODS);
              }
          }
          JCVariableDecl prevReceiverParam = this.receiverParam;
          try {
              this.receiverParam = null;
              // Parsing formalParameters sets the receiverParam, if present
              List<JCVariableDecl> params = List.nil();
              List<JCExpression> thrown = List.nil();
              if (!isRecord || name != names.init || token.kind == LPAREN) {
                  params = formalParameters();
                  if (!isVoid) {
                      type = bracketsOpt(type);
                  }
                  if (token.kind == THROWS) {
                      nextToken();
                      thrown = qualidentList(true);
                  }
              }
              JCBlock body = null;
              JCExpression defaultValue;
              if (token.kind == LBRACE) {
                  body = block();
                  defaultValue = null;
              } else {
                  if (token.kind == DEFAULT) {
                      accept(DEFAULT);
                      defaultValue = annotationValue();
                  } else {
                      defaultValue = null;
                  }
                  accept(SEMI);
                  if (token.pos <= endPosTable.errorEndPos) {
                      // error recovery
                      skip(false, true, false, false);
                      if (token.kind == LBRACE) {
                          body = block();
                      }
                  }
              }
  
              JCMethodDecl result
                      = toP(F.at(pos).MethodDef(mods, name, type, typarams,
                      receiverParam, params, thrown,
                      body, defaultValue));
              attach(result, dc);
              return result;
          } finally {
              this.receiverParam = prevReceiverParam;
          }
      }
  
      protected JCTree methodDeclaratorRest(int pos,
                                            JCModifiers mods,
                                            JCExpression type,
                                            Name name,
                                            List<JCTypeParameter> typarams,
                                            boolean isInterface, boolean isVoid,
                                            Comment dc) {
          return methodDeclaratorRest(pos, mods, type, name, typarams, isInterface, isVoid, false, dc);
      }
  
      /**
       * QualidentList = [Annotations] Qualident {"," [Annotations] Qualident}
       */
      protected List<JCExpression> qualidentList(boolean allowAnnos) {
          ListBuffer<JCExpression> ts = new ListBuffer<>();
  
          List<JCAnnotation> typeAnnos = allowAnnos ? typeAnnotationsOpt() : List.nil();
          JCExpression qi = qualident(allowAnnos);
          if (!typeAnnos.isEmpty()) {
              JCExpression at = insertAnnotationsToMostInner(qi, typeAnnos, false);
              ts.append(at);
          } else {
              ts.append(qi);
          }
          while (token.kind == COMMA) {
              nextToken();
  
              typeAnnos = allowAnnos ? typeAnnotationsOpt() : List.nil();
              qi = qualident(allowAnnos);
              if (!typeAnnos.isEmpty()) {
                  JCExpression at = insertAnnotationsToMostInner(qi, typeAnnos, false);
                  ts.append(at);
              } else {
                  ts.append(qi);
              }
          }
          return ts.toList();
      }
  
      /**
       * {@literal
       * TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"]
       * }
       */
      protected List<JCTypeParameter> typeParametersOpt() {
          if (token.kind == LT) {
              ListBuffer<JCTypeParameter> typarams = new ListBuffer<>();
              nextToken();
              typarams.append(typeParameter());
              while (token.kind == COMMA) {
                  nextToken();
                  typarams.append(typeParameter());
              }
              accept(GT);
              return typarams.toList();
          } else {
              return List.nil();
          }
      }
  
      /**
       * {@literal
       * TypeParameter = [Annotations] TypeVariable [TypeParameterBound]
       * TypeParameterBound = EXTENDS Type {"&" Type}
       * TypeVariable = Ident
       * }
       */
      JCTypeParameter typeParameter() {
          int pos = token.pos;
          List<JCAnnotation> annos = typeAnnotationsOpt();
          Name name = typeName();
          ListBuffer<JCExpression> bounds = new ListBuffer<>();
          if (token.kind == EXTENDS) {
              nextToken();
              bounds.append(parseType());
              while (token.kind == AMP) {
                  nextToken();
                  bounds.append(parseType());
              }
          }
          return toP(F.at(pos).TypeParameter(name, bounds.toList(), annos));
      }
  
      /**
       * FormalParameters = "(" [ FormalParameterList ] ")" FormalParameterList =
       * [ FormalParameterListNovarargs , ] LastFormalParameter
       * FormalParameterListNovarargs = [ FormalParameterListNovarargs , ]
       * FormalParameter
       */
      protected List<JCVariableDecl> formalParameters() {
          return formalParameters(false, false);
      }
  
      protected List<JCVariableDecl> formalParameters(boolean lambdaParameters, boolean recordComponents) {
          ListBuffer<JCVariableDecl> params = new ListBuffer<>();
          JCVariableDecl lastParam;
          accept(LPAREN);
          if (token.kind != RPAREN) {
              this.allowThisIdent = !lambdaParameters && !recordComponents;
              lastParam = formalParameter(lambdaParameters, recordComponents);
              if (lastParam.nameexpr != null) {
                  this.receiverParam = lastParam;
              } else {
                  params.append(lastParam);
              }
              this.allowThisIdent = false;
              while (token.kind == COMMA) {
                  if ((lastParam.mods.flags & Flags.VARARGS) != 0) {
                      log.error(DiagnosticFlag.SYNTAX, lastParam, Errors.VarargsMustBeLast);
                  }
                  nextToken();
                  params.append(lastParam = formalParameter(lambdaParameters, recordComponents));
              }
          }
          if (token.kind == RPAREN) {
              nextToken();
          } else {
              setErrorEndPos(token.pos);
              reportSyntaxError(S.prevToken().endPos, Errors.Expected3(COMMA, RPAREN, LBRACKET));
          }
          return params.toList();
      }
  
      List<JCVariableDecl> implicitParameters(boolean hasParens) {
          if (hasParens) {
              accept(LPAREN);
          }
          ListBuffer<JCVariableDecl> params = new ListBuffer<>();
          if (token.kind != RPAREN && token.kind != ARROW) {
              params.append(implicitParameter());
              while (token.kind == COMMA) {
                  nextToken();
                  params.append(implicitParameter());
              }
          }
          if (hasParens) {
              accept(RPAREN);
          }
          return params.toList();
      }
  
      JCModifiers optFinal(long flags) {
          JCModifiers mods = modifiersOpt();
          checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED));
          mods.flags |= flags;
          return mods;
      }
  
      /**
       * Inserts the annotations (and possibly a new array level) to the left-most
       * type in an array or nested type.
       * <p>
       * When parsing a type like {@code @B Outer.Inner @A []}, the {@code @A}
       * annotation should target the array itself, while {@code @B} targets the
       * nested type {@code Outer}.
       * <p>
       * Currently the parser parses the annotation first, then the array, and
       * then inserts the annotation to the left-most nested type.
       * <p>
       * When {@code createNewLevel} is true, then a new array level is inserted
       * as the most inner type, and have the annotations target it. This is
       * useful in the case of varargs, e.g. {@code String @A [] @B ...}, as the
       * parser first parses the type {@code String @A []} then inserts a new
       * array level with {@code @B} annotation.
       */
      private JCExpression insertAnnotationsToMostInner(
              JCExpression type, List<JCAnnotation> annos,
              boolean createNewLevel) {
          int origEndPos = createNewLevel ? token.endPos : getEndPos(type);
          JCExpression mostInnerType = type;
          JCArrayTypeTree mostInnerArrayType = null;
          while (TreeInfo.typeIn(mostInnerType).hasTag(TYPEARRAY)) {
              mostInnerArrayType = (JCArrayTypeTree)TreeInfo.typeIn(mostInnerType);
              storeEnd(mostInnerType, origEndPos);
              mostInnerType = mostInnerArrayType.elemtype;
          }
  
          if (createNewLevel) {
              mostInnerType = to(F.at(token.pos).TypeArray(mostInnerType));
              origEndPos = getEndPos(mostInnerType);
          }
  
          JCExpression mostInnerTypeToReturn = mostInnerType;
          if (annos.nonEmpty()) {
              JCExpression lastToModify = mostInnerType;
  
              while (TreeInfo.typeIn(mostInnerType).hasTag(SELECT)
                      || TreeInfo.typeIn(mostInnerType).hasTag(TYPEAPPLY)) {
                  while (TreeInfo.typeIn(mostInnerType).hasTag(SELECT)) {
                      lastToModify = mostInnerType;
                      mostInnerType = ((JCFieldAccess)TreeInfo.typeIn(mostInnerType)).getExpression();
                  }
                  while (TreeInfo.typeIn(mostInnerType).hasTag(TYPEAPPLY)) {
                      lastToModify = mostInnerType;
                      mostInnerType = ((JCTypeApply)TreeInfo.typeIn(mostInnerType)).clazz;
                  }
              }
  
              mostInnerType = F.at(annos.head.pos).AnnotatedType(annos, mostInnerType);
  
              if (TreeInfo.typeIn(lastToModify).hasTag(TYPEAPPLY)) {
                  ((JCTypeApply)TreeInfo.typeIn(lastToModify)).clazz = mostInnerType;
              } else if (TreeInfo.typeIn(lastToModify).hasTag(SELECT)) {
                  ((JCFieldAccess)TreeInfo.typeIn(lastToModify)).selected = mostInnerType;
              } else {
                  // We never saw a SELECT or TYPEAPPLY, return the annotated type.
                  mostInnerTypeToReturn = mostInnerType;
              }
          }
  
          if (mostInnerArrayType == null) {
              return mostInnerTypeToReturn;
          } else {
              mostInnerArrayType.elemtype = mostInnerTypeToReturn;
              storeEnd(type, origEndPos);
              return type;
          }
      }
  
      /**
       * FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId
       * LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident |
       * FormalParameter
       */
      protected JCVariableDecl formalParameter(boolean lambdaParameter, boolean recordComponent) {
          JCModifiers mods = !recordComponent ? optFinal(Flags.PARAMETER) : modifiersOpt();
          if (recordComponent && mods.flags != 0) {
              log.error(mods.pos, Errors.RecordCantDeclareFieldModifiers);
          }
          if (recordComponent) {
              mods.flags |= Flags.RECORD | Flags.FINAL | Flags.PRIVATE | Flags.GENERATED_MEMBER;
          }
          // need to distinguish between vararg annos and array annos
          // look at typeAnnotationsPushedBack comment
          this.permitTypeAnnotationsPushBack = true;
          JCExpression type = parseType(lambdaParameter);
          this.permitTypeAnnotationsPushBack = false;
  
          if (token.kind == ELLIPSIS) {
              List<JCAnnotation> varargsAnnos = typeAnnotationsPushedBack;
              typeAnnotationsPushedBack = List.nil();
              mods.flags |= Flags.VARARGS;
              // insert var arg type annotations
              type = insertAnnotationsToMostInner(type, varargsAnnos, true);
              nextToken();
          } else {
              // if not a var arg, then typeAnnotationsPushedBack should be null
              if (typeAnnotationsPushedBack.nonEmpty()) {
                  reportSyntaxError(typeAnnotationsPushedBack.head.pos, Errors.IllegalStartOfType);
              }
              typeAnnotationsPushedBack = List.nil();
          }
          return variableDeclaratorId(mods, type, lambdaParameter, recordComponent);
      }
  
      protected JCVariableDecl implicitParameter() {
          JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
          return variableDeclaratorId(mods, null, true, false);
      }
  
      /* ---------- auxiliary methods -------------- */
  
      /**
       * Check that given tree is a legal expression statement.
       */
      protected JCExpression checkExprStat(JCExpression t) {
          if (!TreeInfo.isExpressionStatement(t, names)) {
              JCExpression ret = F.at(t.pos).Erroneous(List.<JCTree>of(t));
              log.error(DiagnosticFlag.SYNTAX, ret, Errors.NotStmt);
              return ret;
          } else {
              return t;
          }
      }
  
      /**
       * Return precedence of operator represented by token, -1 if token is not a
       * binary operator. @see TreeInfo.opPrec
       */
      static int prec(TokenKind token) {
          JCTree.Tag oc = optag(token);
          return (oc != NO_TAG) ? TreeInfo.opPrec(oc) : -1;
      }
  
      /**
       * Return the lesser of two positions, making allowance for either one being
       * unset.
       */
      static int earlier(int pos1, int pos2) {
          if (pos1 == Position.NOPOS) {
              return pos2;
          }
          if (pos2 == Position.NOPOS) {
              return pos1;
          }
          return (pos1 < pos2 ? pos1 : pos2);
      }
  
      /**
       * Return operation tag of binary operator represented by token, No_TAG if
       * token is not a binary operator.
       */
      static JCTree.Tag optag(TokenKind token) {
          switch (token) {
              case BARBAR:
                  return OR;
              case AMPAMP:
                  return AND;
              case BAR:
                  return BITOR;
              case BAREQ:
                  return BITOR_ASG;
              case CARET:
                  return BITXOR;
              case CARETEQ:
                  return BITXOR_ASG;
              case AMP:
                  return BITAND;
              case AMPEQ:
                  return BITAND_ASG;
              case EQEQ:
                  return JCTree.Tag.EQ;
              case BANGEQ:
                  return NE;
              case LT:
                  return JCTree.Tag.LT;
              case GT:
                  return JCTree.Tag.GT;
              case LTEQ:
                  return LE;
              case GTEQ:
                  return GE;
              case LTLT:
                  return SL;
              case LTLTEQ:
                  return SL_ASG;
              case GTGT:
                  return SR;
              case GTGTEQ:
                  return SR_ASG;
              case GTGTGT:
                  return USR;
              case GTGTGTEQ:
                  return USR_ASG;
              case PLUS:
                  return JCTree.Tag.PLUS;
              case PLUSEQ:
                  return PLUS_ASG;
              case SUB:
                  return MINUS;
              case SUBEQ:
                  return MINUS_ASG;
              case STAR:
                  return MUL;
              case STAREQ:
                  return MUL_ASG;
              case SLASH:
                  return DIV;
              case SLASHEQ:
                  return DIV_ASG;
              case PERCENT:
                  return MOD;
              case PERCENTEQ:
                  return MOD_ASG;
              case INSTANCEOF:
                  return TYPETEST;
              default:
                  return NO_TAG;
          }
      }
  
      /**
       * Return operation tag of unary operator represented by token, No_TAG if
       * token is not a binary operator.
       */
      static JCTree.Tag unoptag(TokenKind token) {
          switch (token) {
              case PLUS:
                  return POS;
              case SUB:
                  return NEG;
              case BANG:
                  return NOT;
              case TILDE:
                  return COMPL;
              case PLUSPLUS:
                  return PREINC;
              case SUBSUB:
                  return PREDEC;
              default:
                  return NO_TAG;
          }
      }
  
      /**
       * Return type tag of basic type represented by token, NONE if token is not
       * a basic type identifier.
       */
      static TypeTag typetag(TokenKind token) {
          switch (token) {
              case BYTE:
                  return TypeTag.BYTE;
              case CHAR:
                  return TypeTag.CHAR;
              case SHORT:
                  return TypeTag.SHORT;
              case INT:
                  return TypeTag.INT;
              case LONG:
                  return TypeTag.LONG;
              case FLOAT:
                  return TypeTag.FLOAT;
              case DOUBLE:
                  return TypeTag.DOUBLE;
              case BOOLEAN:
                  return TypeTag.BOOLEAN;
              default:
                  return TypeTag.NONE;
          }
      }
  
      protected void checkSourceLevel(Feature feature) {
          checkSourceLevel(token.pos, feature);
      }
  
      protected void checkSourceLevel(int pos, Feature feature) {
          if (preview.isPreview(feature) && !preview.isEnabled()) {
              //preview feature without --preview flag, error
              log.error(DiagnosticFlag.SOURCE_LEVEL, pos, preview.disabledError(feature));
          } else if (!feature.allowedInSource(source)) {
              //incompatible source level, error
              log.error(DiagnosticFlag.SOURCE_LEVEL, pos, feature.error(source.name));
          } else if (preview.isPreview(feature)) {
              //use of preview feature, warn
              preview.warnPreview(pos, feature);
          }
      }
  
      /*
       * a functional source tree and end position mappings
       */
      protected static class SimpleEndPosTable extends AbstractEndPosTable {
  
          private final IntHashTable endPosMap;
  
          protected SimpleEndPosTable(JavacParser parser) {
              super(parser);
              endPosMap = new IntHashTable();
          }
  
          public void storeEnd(JCTree tree, int endpos) {
  
              if (tree instanceof JCIdent) {
                  JCIdent i = (JCIdent)tree;
  
                  if (i.pos + (i.name != null ? i.name.length() : 0) == endpos) {
                      return;
                  }
              } else if (tree instanceof JCFieldAccess) {
                  JCFieldAccess f = (JCFieldAccess)tree;
  
                  if (f.pos + (f.name != null ? f.name.length() : 0) + 1 == endpos) {
                      return;
                  }
              }
              endPosMap.put(tree, Math.max(errorEndPos, endpos));
          }
  
          private void removeEndPos(JCTree tree) {
              endPosMap.remove(tree);
          }
  
          protected <T extends JCTree> T to(T t) {
              storeEnd(t, parser.token.endPos);
              return t;
          }
  
          protected <T extends JCTree> T toP(T t) {
              storeEnd(t, parser.S.prevToken().endPos);
              return t;
          }
  
          public int getEndPos(JCTree tree) {
              int value = endPosMap.get(tree);
              // As long as Position.NOPOS==-1, this just returns value.
              return (value == -1) ? Position.NOPOS : value;
          }
  
          public int replaceTree(JCTree oldTree, JCTree newTree) {
              int pos = endPosMap.remove(oldTree);
              if (pos != -1) {
                  storeEnd(newTree, pos);
                  return pos;
              }
              return Position.NOPOS;
          }
      }
  
      /*
       * a default skeletal implementation without any mapping overhead.
       */
      protected static class EmptyEndPosTable extends AbstractEndPosTable {
  
          EmptyEndPosTable(JavacParser parser) {
              super(parser);
          }
  
          public void storeEnd(JCTree tree, int endpos) {
              /* empty */
          }
  
          protected <T extends JCTree> T to(T t) {
              return t;
          }
  
          protected <T extends JCTree> T toP(T t) {
              return t;
          }
  
          public int getEndPos(JCTree tree) {
              return Position.NOPOS;
          }
  
          public int replaceTree(JCTree oldTree, JCTree newTree) {
              return Position.NOPOS;
          }
  
      }
  
      protected static abstract class AbstractEndPosTable implements EndPosTable {
  
          /**
           * The current parser.
           */
          protected JavacParser parser;
  
          /**
           * Store the last error position.
           */
          public int errorEndPos = Position.NOPOS;
  
          public AbstractEndPosTable(JavacParser parser) {
              this.parser = parser;
          }
  
          /**
           * Store current token's ending position for a tree, the value of which
           * will be the greater of last error position and the ending position of
           * the current token.
           *
           * @param t The tree.
           */
          protected abstract <T extends JCTree> T to(T t);
  
          /**
           * Store current token's ending position for a tree, the value of which
           * will be the greater of last error position and the ending position of
           * the previous token.
           *
           * @param t The tree.
           */
          protected abstract <T extends JCTree> T toP(T t);
  
          /**
           * Set the error position during the parsing phases, the value of which
           * will be set only if it is greater than the last stored error
           * position.
           *
           * @param errPos The error position
           */
          public void setErrorEndPos(int errPos) {
              if (errPos > errorEndPos) {
                  errorEndPos = errPos;
              }
          }
  
          public void setParser(JavacParser parser) {
              this.parser = parser;
          }
      }
  }
  