< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java

Print this page
@@ -33,10 +33,11 @@
  import com.sun.source.tree.CaseTree;
  import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
  import com.sun.source.tree.ModuleTree.ModuleKind;
  
  import com.sun.tools.javac.code.*;
+ import com.sun.tools.javac.code.Flags.Flag;
  import com.sun.tools.javac.code.Source.Feature;
  import com.sun.tools.javac.parser.Tokens.*;
  import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
  import com.sun.tools.javac.resources.CompilerProperties.Errors;
  import com.sun.tools.javac.resources.CompilerProperties.Fragments;

@@ -47,18 +48,20 @@
  import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
  import com.sun.tools.javac.util.JCDiagnostic.Error;
  import com.sun.tools.javac.util.JCDiagnostic.Fragment;
  import com.sun.tools.javac.util.List;
  
+ import static com.sun.tools.javac.code.Flags.asFlagSet;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.*;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.ASSERT;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.CASE;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.CATCH;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.EQ;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.GT;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
  import static com.sun.tools.javac.parser.Tokens.TokenKind.LT;
+ import static com.sun.tools.javac.parser.Tokens.TokenKind.SYNCHRONIZED;
  import static com.sun.tools.javac.tree.JCTree.Tag.*;
  import static com.sun.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
  import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
  import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
  import java.util.function.BiFunction;

@@ -183,13 +186,16 @@
          this.parseModuleInfo = parseModuleInfo;
          docComments = newDocCommentTable(keepDocComments, fac);
          this.keepLineMap = keepLineMap;
          this.errorTree = F.Erroneous();
          endPosTable = newEndPosTable(keepEndPositions);
+         this.allowWithFieldOperator = fac.options.isSet("allowWithFieldOperator");
          this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source);
          this.allowRecords = Feature.RECORDS.allowedInSource(source);
          this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
+         this.allowPrimitiveClasses = (!preview.isPreview(Feature.PRIMITIVE_CLASSES) || preview.isEnabled()) &&
+                 Feature.PRIMITIVE_CLASSES.allowedInSource(source);
      }
  
      protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
          return  keepEndPositions
                  ? new SimpleEndPosTable(this)

@@ -202,10 +208,14 @@
  
      /** Switch: should we fold strings?
       */
      boolean allowStringFolding;
  
+     /** Switch: should we allow withField operator at source level ?
+     */
+     boolean allowWithFieldOperator;
+ 
      /** Switch: should we keep docComments?
       */
      boolean keepDocComments;
  
      /** Switch: should we keep line table?

@@ -223,10 +233,14 @@
  
      /** Switch: are records allowed in this source level?
       */
      boolean allowRecords;
  
+     /** Switch: are primitive classes allowed in this source level?
+      */
+      boolean allowPrimitiveClasses;
+ 
      /** Switch: are sealed types allowed in this source level?
       */
      boolean allowSealedTypes;
  
      /** The type of the method receiver, as specified by a first "this" parameter.

@@ -301,10 +315,17 @@
          return tk1.test(S.token(lookahead + 1).kind) &&
                  tk2.test(S.token(lookahead + 2).kind) &&
                  tk3.test(S.token(lookahead + 3).kind);
      }
  
+     protected boolean peekToken(int lookahead, Predicate<TokenKind> tk1, Predicate<TokenKind> tk2, Predicate<TokenKind> tk3, Predicate<TokenKind> tk4) {
+         return tk1.test(S.token(lookahead + 1).kind) &&
+                 tk2.test(S.token(lookahead + 2).kind) &&
+                 tk3.test(S.token(lookahead + 3).kind) &&
+                 tk4.test(S.token(lookahead + 4).kind);
+     }
+ 
      @SuppressWarnings("unchecked")
      protected boolean peekToken(Predicate<TokenKind>... kinds) {
          return peekToken(0, kinds);
      }
  

@@ -470,10 +491,26 @@
              setErrorEndPos(token.pos);
              reportSyntaxError(S.prevToken().endPos, errorProvider.apply(tk));
          }
      }
  
+     /** If next input token matches one of the two given tokens, skip it, otherwise report
+      *  an error.
+      *
+      * @return The actual token kind.
+      */
+     public TokenKind accept2(TokenKind tk1, TokenKind tk2) {
+         TokenKind returnValue = token.kind;
+         if (token.kind == tk1 || token.kind == tk2) {
+             nextToken();
+         } else {
+             setErrorEndPos(token.pos);
+             reportSyntaxError(S.prevToken().endPos, Errors.Expected2(tk1, tk2));
+         }
+         return returnValue;
+     }
+ 
      /** Report an illegal start of expression/type error at given position.
       */
      JCExpression illegal(int pos) {
          setErrorEndPos(pos);
          if ((mode & EXPR) != 0)

@@ -1170,10 +1207,25 @@
                      t = term3();
                      return F.at(pos).Unary(unoptag(tk), t);
                  }
              } else return illegal();
              break;
+         case WITHFIELD:
+             if (!allowWithFieldOperator) {
+                 log.error(pos, Errors.WithFieldOperatorDisallowed);
+             }
+             if (typeArgs == null && (mode & EXPR) != 0) {
+                 nextToken();
+                 accept(LPAREN);
+                 mode = EXPR;
+                 t = term();
+                 accept(COMMA);
+                 mode = EXPR;
+                 JCExpression v = term();
+                 accept(RPAREN);
+                 return F.at(pos).WithField(t, v);
+             } else return illegal();
          case LPAREN:
              if (typeArgs == null && (mode & EXPR) != 0) {
                  ParensResult pres = analyzeParens();
                  switch (pres) {
                      case CAST:

@@ -1338,10 +1390,16 @@
                          mode &= ~NOPARAMS;
                          typeArgs = typeArgumentsOpt(EXPR);
                          mode = oldmode;
                          if ((mode & EXPR) != 0) {
                              switch (token.kind) {
+                             case DEFAULT:
+                                 if (typeArgs != null) return illegal();
+                                 selectExprMode();
+                                 t = to(F.at(pos).DefaultValue(t));
+                                 nextToken();
+                                 break loop;
                              case CLASS:
                                  if (typeArgs != null) return illegal();
                                  selectExprMode();
                                  t = to(F.at(pos).Select(t, names._class));
                                  nextToken();

@@ -1395,13 +1453,14 @@
                              // 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
+                         if ((mode & TYPE) == 0 && isParameterizedTypePrefix()) {
+                             //this is either an unbound method reference whose qualifier
+                             //is a generic type i.e. A<S>::m or a default value creation of
+                             //the form ValueType<S>.default
                              int pos1 = token.pos;
                              accept(LT);
                              ListBuffer<JCExpression> args = new ListBuffer<>();
                              args.append(typeArgument());
                              while (token.kind == COMMA) {

@@ -1410,10 +1469,16 @@
                              }
                              accept(GT);
                              t = toP(F.at(pos1).TypeApply(t, args.toList()));
                              while (token.kind == DOT) {
                                  nextToken();
+                                 if (token.kind == DEFAULT) {
+                                     t =  toP(F.at(token.pos).DefaultValue(t));
+                                     nextToken();
+                                     selectExprMode();
+                                     return term3Rest(t, typeArgs);
+                                 }
                                  selectTypeMode();
                                  t = toP(F.at(token.pos).Select(t, ident()));
                                  t = typeArgumentsOpt(t);
                              }
                              t = bracketsOpt(t);

@@ -1576,11 +1641,11 @@
                      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) {
+                 } 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);

@@ -1630,15 +1695,16 @@
          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
+      * method reference or a default value creation that uses a parameterized type
+      * or a binary expression. To disambiguate, look for a
       * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
       */
      @SuppressWarnings("fallthrough")
-     boolean isUnboundMemberRef() {
+     boolean isParameterizedTypePrefix() {
          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:

@@ -1756,12 +1822,12 @@
                      }
                  case UNDERSCORE:
                  case ASSERT:
                  case ENUM:
                  case IDENTIFIER:
-                     if (peekToken(lookahead, LAX_IDENTIFIER)) {
-                         // Identifier, Identifier/'_'/'assert'/'enum' -> explicit lambda
+                     if (peekToken(lookahead, LAX_IDENTIFIER) || (peekToken(lookahead, QUES, LAX_IDENTIFIER) && (peekToken(lookahead + 2, RPAREN) || peekToken(lookahead + 2, COMMA)))) {
+                         // 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;

@@ -1809,10 +1875,12 @@
                              // '>', ')' -> cast
                              // '>', '&' -> cast
                              return ParensResult.CAST;
                          } else if (peekToken(lookahead, LAX_IDENTIFIER, COMMA) ||
                                  peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW) ||
+                                 peekToken(lookahead, QUES, LAX_IDENTIFIER, COMMA) ||
+                                 peekToken(lookahead, QUES, LAX_IDENTIFIER, RPAREN, ARROW) ||
                                  peekToken(lookahead, ELLIPSIS)) {
                              // '>', Identifier/'_'/'assert'/'enum', ',' -> explicit lambda
                              // '>', Identifier/'_'/'assert'/'enum', ')', '->' -> explicit lambda
                              // '>', '...' -> explicit lambda
                              return ParensResult.EXPLICIT_LAMBDA;

@@ -2234,11 +2302,11 @@
      JCExpression bracketsSuffix(JCExpression t) {
          if ((mode & EXPR) != 0 && token.kind == DOT) {
              selectExprMode();
              int pos = token.pos;
              nextToken();
-             accept(CLASS);
+             TokenKind selector = accept2(CLASS, DEFAULT);
              if (token.pos == endPosTable.errorEndPos) {
                  // error recovery
                  Name name;
                  if (LAX_IDENTIFIER.test(token.kind)) {
                      name = token.name();

@@ -2252,11 +2320,15 @@
                  // 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));
+                 if (selector == CLASS) {
+                     t = toP(F.at(pos).Select(t, names._class));
+                 } else {
+                     t = toP(F.at(pos).DefaultValue(t));
+                 }
              }
          } else if ((mode & TYPE) != 0) {
              if (token.kind != COLCOL) {
                  selectTypeMode();
              }

@@ -2297,15 +2369,22 @@
      }
  
      /** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
       */
      JCExpression creator(int newpos, List<JCExpression> typeArgs) {
-         List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
- 
+         final JCModifiers mods = modifiersOpt();
+         List<JCAnnotation> newAnnotations = mods.annotations;
+         if (!newAnnotations.isEmpty()) {
+             checkSourceLevel(newAnnotations.head.pos, Feature.TYPE_ANNOTATIONS);
+         }
          switch (token.kind) {
          case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
          case DOUBLE: case BOOLEAN:
+             if (mods.flags != 0) {
+                 long badModifiers = (mods.flags & Flags.PRIMITIVE_CLASS) != 0 ? mods.flags & ~(Flags.FINAL | Flags.REFERENCE_FAVORING) : mods.flags;
+                 log.error(token.pos, Errors.ModNotAllowedHere(asFlagSet(badModifiers)));
+             }
              if (typeArgs == null) {
                  if (newAnnotations.isEmpty()) {
                      return arrayCreatorRest(newpos, basicType());
                  } else {
                      return arrayCreatorRest(newpos, toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, basicType())));

@@ -2370,15 +2449,23 @@
                  reportSyntaxError(err, Errors.CannotCreateArrayWithTypeArguments);
                  return toP(err);
              }
              return e;
          } else if (token.kind == LPAREN) {
+             long badModifiers = mods.flags & ~(Flags.PRIMITIVE_CLASS | Flags.FINAL | Flags.REFERENCE_FAVORING);
+             if (badModifiers != 0)
+                 log.error(token.pos, Errors.ModNotAllowedHere(asFlagSet(badModifiers)));
              // handle type annotations for instantiations and anonymous classes
              if (newAnnotations.nonEmpty()) {
                  t = insertAnnotationsToMostInner(t, newAnnotations, false);
              }
-             return classCreatorRest(newpos, null, typeArgs, t);
+             JCNewClass newClass = classCreatorRest(newpos, null, typeArgs, t, mods.flags);
+             if ((newClass.def == null) && (mods.flags != 0)) {
+                 badModifiers = (mods.flags & Flags.PRIMITIVE_CLASS) != 0 ? mods.flags & ~(Flags.FINAL | Flags.REFERENCE_FAVORING) : mods.flags;
+                 log.error(newClass.pos, Errors.ModNotAllowedHere(asFlagSet(badModifiers)));
+             }
+             return newClass;
          } 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)));

@@ -2399,11 +2486,11 @@
          if (token.kind == LT) {
              int oldmode = mode;
              t = typeArguments(t, true);
              mode = oldmode;
          }
-         return classCreatorRest(newpos, encl, typeArgs, t);
+         return classCreatorRest(newpos, encl, typeArgs, t, 0);
      }
  
      /** ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer
       *                         | Expression "]" {[Annotations]  "[" Expression "]"} BracketsOpt )
       */

@@ -2477,21 +2564,23 @@
      /** ClassCreatorRest = Arguments [ClassBody]
       */
      JCNewClass classCreatorRest(int newpos,
                                    JCExpression encl,
                                    List<JCExpression> typeArgs,
-                                   JCExpression t)
+                                   JCExpression t,
+                                   long flags)
      {
          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);
+             JCModifiers mods = F.at(Position.NOPOS).Modifiers(flags);
              body = toP(F.at(pos).AnonymousClassDef(mods, defs));
          }
-         return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
+         JCNewClass newClass = toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
+         return newClass;
      }
  
      /** ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}] [","] "}"
       */
      JCExpression arrayInitializer(int newpos, JCExpression t) {

@@ -2720,10 +2809,14 @@
                      nextToken();
                      return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), token.comment(CommentStyle.JAVADOC)));
                  }
              }
          }
+         if (isPrimitiveModifier()) {
+             dc = token.comment(CommentStyle.JAVADOC);
+             return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
+         }
          if (isRecordStart() && allowRecords) {
              dc = token.comment(CommentStyle.JAVADOC);
              return List.of(recordDeclaration(F.at(pos).Modifiers(0), dc));
          } else {
              Token prevToken = token;

@@ -3157,11 +3250,14 @@
          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)) {
-                 return variableDeclarators(modifiersOpt(), t, stats, true).toList();
+                 pos = token.pos;
+                 JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+                 F.at(pos);
+                 return variableDeclarators(mods, 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();

@@ -3254,10 +3350,14 @@
                  if (isSealedClassStart(false)) {
                      checkSourceLevel(Feature.SEALED_CLASSES);
                      flag = Flags.SEALED;
                      break;
                  }
+                 if (isPrimitiveModifier()) {
+                     flag = Flags.PRIMITIVE_CLASS;
+                     break;
+                 }
                  break loop;
              }
              default: break loop;
              }
              if ((flags & flag) != 0) log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.RepeatedModifier);

@@ -3267,12 +3367,17 @@
                  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;
+                     final Name name = TreeInfo.name(ann.annotationType);
+                     if (name == names.__primitive__ || name == names.java_lang___primitive__) {
+                         flag = Flags.PRIMITIVE_CLASS;
+                     } else {
+                         annotations.append(ann);
+                         flag = 0;
+                     }
                  }
              }
              flags |= flag;
          }
          switch (token.kind) {

@@ -3284,10 +3389,15 @@
          /* A modifiers tree with no modifier tokens or annotations
           * has no text position. */
          if ((flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0 && annotations.isEmpty())
              pos = Position.NOPOS;
  
+         // Force primitive classes to be automatically final.
+         if ((flags & (Flags.PRIMITIVE_CLASS | Flags.ABSTRACT | Flags.INTERFACE | Flags.ENUM)) == Flags.PRIMITIVE_CLASS) {
+             flags |= Flags.FINAL;
+         }
+ 
          JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList());
          if (pos != Position.NOPOS)
              storeEnd(mods, S.prevToken().endPos);
          return mods;
      }

@@ -3508,10 +3618,17 @@
                  return Source.JDK14;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14));
              }
          }
+         if (name == names.primitive) {
+             if (allowPrimitiveClasses) {
+                 return Source.JDK17;
+             } else if (shouldWarn) {
+                 log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK17));
+             }
+         }
          if (name == names.sealed) {
              if (allowSealedTypes) {
                  return Source.JDK15;
              } else if (shouldWarn) {
                  log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));

@@ -3908,10 +4025,20 @@
      protected JCClassDecl classDeclaration(JCModifiers mods, Comment dc) {
          int pos = token.pos;
          accept(CLASS);
          Name name = typeName();
  
+         if ((mods.flags & Flags.PRIMITIVE_CLASS) != 0) {
+             if (token.kind == DOT) {
+                 final Token pastDot = S.token(1);
+                 if (pastDot.kind == IDENTIFIER && pastDot.name() == names.val) {
+                     nextToken(); nextToken(); // discard .val
+                     mods.flags |= Flags.REFERENCE_FAVORING;
+                 }
+             }
+         }
+ 
          List<JCTypeParameter> typarams = typeParametersOpt();
  
          JCExpression extending = null;
          if (token.kind == EXTENDS) {
              nextToken();

@@ -3933,10 +4060,19 @@
      protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) {
          int pos = token.pos;
          nextToken();
          mods.flags |= Flags.RECORD;
          Name name = typeName();
+         if ((mods.flags & Flags.PRIMITIVE_CLASS) != 0) {
+             if (token.kind == DOT) {
+                 final Token pastDot = S.token(1);
+                 if (pastDot.kind == IDENTIFIER && pastDot.name() == names.val) {
+                     nextToken(); nextToken(); // discard .val
+                     mods.flags |= Flags.REFERENCE_FAVORING;
+                 }
+             }
+         }
  
          List<JCTypeParameter> typarams = typeParametersOpt();
  
          List<JCVariableDecl> headerFields = formalParameters(false, true);
  

@@ -4368,10 +4504,11 @@
  
      protected boolean isRecordStart() {
          if (token.kind == IDENTIFIER && token.name() == names.record &&
              (peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) ||
               peekToken(TokenKind.IDENTIFIER, TokenKind.EOF) ||
+              peekToken(TokenKind.IDENTIFIER, TokenKind.DOT) ||
               peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
              checkSourceLevel(Feature.RECORDS);
              return true;
          } else {
              return false;

@@ -4398,10 +4535,26 @@
              }
          }
          return false;
      }
  
+     protected boolean isPrimitiveModifier() {
+         if (allowPrimitiveClasses && token.kind == IDENTIFIER && token.name() == names.primitive) {
+             Token next = S.token(1);
+             switch (next.kind) {
+                 case PRIVATE: case PROTECTED: case PUBLIC: case STATIC: case TRANSIENT:
+                 case FINAL: case ABSTRACT: case NATIVE: case VOLATILE: case SYNCHRONIZED:
+                 case STRICTFP: case MONKEYS_AT: case DEFAULT: case BYTE: case SHORT:
+                 case CHAR: case INT: case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
+                 case CLASS: case INTERFACE: case ENUM: case IDENTIFIER: // new primitive Comparable() {}
+                     checkSourceLevel(Feature.PRIMITIVE_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);
< prev index next >