< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

Print this page
@@ -26,11 +26,10 @@
  package com.sun.tools.javac.comp;
  
  import java.util.*;
  import java.util.function.BiConsumer;
  import java.util.function.BiPredicate;
- import java.util.function.Consumer;
  import java.util.function.Predicate;
  import java.util.function.Supplier;
  import java.util.function.ToIntBiFunction;
  import java.util.stream.Collectors;
  import java.util.stream.StreamSupport;

@@ -77,15 +76,12 @@
  import static com.sun.tools.javac.code.TypeTag.*;
  import static com.sun.tools.javac.code.TypeTag.WILDCARD;
  
  import static com.sun.tools.javac.tree.JCTree.Tag.*;
  import javax.lang.model.element.Element;
- import javax.lang.model.element.ExecutableElement;
  import javax.lang.model.element.TypeElement;
  import javax.lang.model.type.DeclaredType;
- import javax.lang.model.type.TypeMirror;
- import javax.lang.model.util.ElementFilter;
  import javax.lang.model.util.ElementKindVisitor14;
  
  /** Type checking helper class for the attribution phase.
   *
   *  <p><b>This is NOT part of any supported API.

@@ -182,10 +178,12 @@
          deferredLintHandler = DeferredLintHandler.instance(context);
  
          allowModules = Feature.MODULES.allowedInSource(source);
          allowRecords = Feature.RECORDS.allowedInSource(source);
          allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
+         allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
+                 Feature.VALUE_CLASSES.allowedInSource(source);
      }
  
      /** Character for synthetic names
       */
      char syntheticNameChar;

@@ -225,10 +223,14 @@
  
      /** Are sealed classes allowed
       */
      private final boolean allowSealed;
  
+     /** Are value classes allowed
+      */
+     private final boolean allowValueClasses;
+ 
  /* *************************************************************************
   * Errors and Warnings
   **************************************************************************/
  
      Lint setLint(Lint newLint) {

@@ -758,10 +760,35 @@
              return (t.hasTag(TYPEVAR))
                                      ? diags.fragment(Fragments.TypeParameter(t))
                                      : t;
          }
  
+     void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
+         DiagnosticPosition pos = tree.pos();
+         for (Type st : types.closure(c.type)) {
+             if (st == null || st.tsym == null || st.tsym.kind == ERR)
+                 continue;
+             if  (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
+                 continue;
+             if (!st.tsym.isAbstract()) {
+                 if (c != st.tsym) {
+                     log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
+                 }
+                 continue;
+             }
+             // dealing with an abstract value or value super class below.
+             for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
+                 if (s.kind == MTH) {
+                     if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
+                         log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
+                     }
+                     break;
+                 }
+             }
+         }
+     }
+ 
      /** Check that type is a valid qualifier for a constructor reference expression
       */
      Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
          t = checkClassOrArrayType(pos, t);
          if (t.hasTag(CLASS)) {

@@ -815,10 +842,36 @@
              return typeTagError(pos,
                                  diags.fragment(Fragments.TypeReqRef),
                                  t);
      }
  
+     /** Check that type is an identity type, i.e. not a value type.
+      *  When not discernible statically, give it the benefit of doubt
+      *  and defer to runtime.
+      *
+      *  @param pos           Position to be used for error reporting.
+      *  @param t             The type to be checked.
+      */
+     boolean checkIdentityType(DiagnosticPosition pos, Type t) {
+         if (t.hasTag(TYPEVAR)) {
+             t = types.skipTypeVars(t, false);
+         }
+         if (t.isIntersection()) {
+             IntersectionClassType ict = (IntersectionClassType)t;
+             boolean result = true;
+             for (Type component : ict.getExplicitComponents()) {
+                 result &= checkIdentityType(pos, component);
+             }
+             return result;
+         }
+         if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
+             typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
+             return false;
+         }
+         return true;
+     }
+ 
      /** Check that each type is a reference type, i.e. a class, interface or array type
       *  or a type variable.
       *  @param trees         Original trees, used for error reporting.
       *  @param types         The types to be checked.
       */

@@ -1205,12 +1258,17 @@
                  mask = ReceiverParamFlags;
              else if (sym.owner.kind != TYP)
                  mask = LocalVarFlags;
              else if ((sym.owner.flags_field & INTERFACE) != 0)
                  mask = implicit = InterfaceVarFlags;
-             else
-                 mask = VarFlags;
+             else {
+                 boolean isInstanceFieldOfValueClass = sym.owner.type.isValueClass() && (flags & STATIC) == 0;
+                 mask = !isInstanceFieldOfValueClass ? VarFlags : ExtendedVarFlags;
+                 if (isInstanceFieldOfValueClass) {
+                     implicit |= FINAL | STRICT;
+                 }
+             }
              break;
          case MTH:
              if (sym.name == names.init) {
                  if ((sym.owner.flags_field & ENUM) != 0) {
                      // enum constructors cannot be declared public or

@@ -1232,13 +1290,16 @@
                      }
                  } else {
                      mask = implicit = InterfaceMethodFlags;
                  }
              } else if ((sym.owner.flags_field & RECORD) != 0) {
-                 mask = RecordMethodFlags;
+                 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                         RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
              } else {
-                 mask = MethodFlags;
+                 // value objects do not have an associated monitor/lock
+                 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                         MethodFlags & ~SYNCHRONIZED : MethodFlags;
              }
              if ((flags & STRICTFP) != 0) {
                  warnOnExplicitStrictfp(pos);
              }
              // Imply STRICTFP if owner has STRICTFP set.

@@ -1251,11 +1312,11 @@
                      (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
                  boolean implicitlyStatic = !sym.isAnonymous() &&
                          ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
                  boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
                  // local statics are allowed only if records are allowed too
-                 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
+                 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
                  implicit = implicitlyStatic ? STATIC : implicit;
              } else if (sym.owner.kind == TYP) {
                  // statics in inner classes are allowed only if records are allowed too
                  mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
                  if (sym.owner.owner.kind == PCK ||

@@ -1270,13 +1331,17 @@
                  mask = ExtendedClassFlags;
              }
              // Interfaces are always ABSTRACT
              if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
  
+             if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
+                 implicit |= IDENTITY_TYPE;
+             }
+ 
              if ((flags & ENUM) != 0) {
-                 // enums can't be declared abstract, final, sealed or non-sealed
-                 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
+                 // enums can't be declared abstract, final, sealed or non-sealed or value
+                 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
                  implicit |= implicitEnumFinalFlag(tree);
              }
              if ((flags & RECORD) != 0) {
                  // records can't be declared abstract
                  mask &= ~ABSTRACT;

@@ -1285,10 +1350,15 @@
              if ((flags & STRICTFP) != 0) {
                  warnOnExplicitStrictfp(pos);
              }
              // Imply STRICTFP if owner has STRICTFP set.
              implicit |= sym.owner.flags_field & STRICTFP;
+ 
+             // concrete value classes are implicitly final
+             if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
+                 implicit |= FINAL;
+             }
              break;
          default:
              throw new AssertionError();
          }
          long illegal = flags & ExtendedStandardFlags & ~mask;

@@ -1339,11 +1409,20 @@
                   && checkDisjoint(pos, flags,
                                  SEALED,
                             FINAL | NON_SEALED)
                   && checkDisjoint(pos, flags,
                                  SEALED,
-                                 ANNOTATION)) {
+                                 ANNOTATION)
+                 && checkDisjoint(pos, flags,
+                                 VALUE_CLASS,
+                                 ANNOTATION)
+                 && checkDisjoint(pos, flags,
+                                 VALUE_CLASS,
+                                 NON_SEALED)
+                 && checkDisjoint(pos, flags,
+                                 VALUE_CLASS,
+                                 INTERFACE) ) {
              // skip
          }
          return flags & (mask | ~ExtendedStandardFlags) | implicit;
      }
  

@@ -2156,10 +2235,15 @@
              if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
                  log.error(tree.pos(), Errors.EnumNoFinalize);
                  return;
              }
          }
+         if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
+             if (m.overrides(syms.objectFinalize, origin, types, false)) {
+                 log.warning(tree.pos(), Warnings.ValueFinalize);
+             }
+         }
          if (allowRecords && origin.isRecord()) {
              // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
              Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
                      .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
              if (recordComponent.isPresent()) {

@@ -2584,10 +2668,22 @@
              for (List<Type> m = supertypes; m != l; m = m.tail)
                  if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
                      return;
          }
          checkCompatibleConcretes(pos, c);
+ 
+         Type identitySuper = null;
+         for (Type t : types.closure(c)) {
+             if (t != c) {
+                 if (t.isIdentityClass() && (t.tsym.flags() & VALUE_BASED) == 0)
+                     identitySuper = t;
+                 if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
+                     log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
+                     break;
+                 }
+             }
+         }
      }
  
      /** Check that all non-override equivalent methods accessible from 'site'
       *  are mutually compatible (JLS 8.4.8/9.4.1).
       *

@@ -4896,12 +4992,12 @@
      }
  
      /**
       * Check structure of serialization declarations.
       */
-     public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
-         (new SerialTypeVisitor()).visit(c, tree);
+     public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
+         (new SerialTypeVisitor(env)).visit(c, tree);
      }
  
      /**
       * This visitor will warn if a serialization-related field or
       * method is declared in a suspicious or incorrect way. In

@@ -4928,12 +5024,14 @@
       * Externalizable: methods defined on the interface
       * public void writeExternal(ObjectOutput) throws IOException
       * public void readExternal(ObjectInput) throws IOException
       */
      private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
-         SerialTypeVisitor() {
+         Env<AttrContext> env;
+         SerialTypeVisitor(Env<AttrContext> env) {
              this.lint = Check.this.lint;
+             this.env = env;
          }
  
          private static final Set<String> serialMethodNames =
              Set.of("writeObject", "writeReplace",
                     "readObject",  "readObjectNoData",

@@ -4989,10 +5087,11 @@
                       .iterator()
                       .hasNext();
  
              // Check declarations of serialization-related methods and
              // fields
+             final boolean[] hasWriteReplace = {false};
              for(Symbol el : c.getEnclosedElements()) {
                  runUnderLint(el, p, (enclosed, tree) -> {
                      String name = null;
                      switch(enclosed.getKind()) {
                      case FIELD -> {

@@ -5063,29 +5162,64 @@
                          var method = (MethodSymbol)enclosed;
                          name = method.getSimpleName().toString();
                          if (serialMethodNames.contains(name)) {
                              switch (name) {
                              case "writeObject"      -> checkWriteObject(tree, e, method);
-                             case "writeReplace"     -> checkWriteReplace(tree,e, method);
+                             case "writeReplace"     -> {hasWriteReplace[0] = true; hasAppropriateWriteReplace(tree, method, true);}
                              case "readObject"       -> checkReadObject(tree,e, method);
                              case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
                              case "readResolve"      -> checkReadResolve(tree, e, method);
                              default ->  throw new AssertionError();
                              }
                          }
                      }
                      }
                  });
              }
- 
+             if (!hasWriteReplace[0] &&
+                     (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
+                     !c.isAbstract() && !c.isRecord() &&
+                     types.unboxedType(c.type) == Type.noType) {
+                 // we need to check if the class is inheriting an appropriate writeReplace method
+                 MethodSymbol ms = null;
+                 Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log);
+                 try {
+                     ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
+                 } catch (FatalError fe) {
+                     // ignore no method was found
+                 } finally {
+                     log.popDiagnosticHandler(discardHandler);
+                 }
+                 if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
+                     log.warning(LintCategory.SERIAL, p,
+                             c.isValueClass() ? Warnings.SerializableValueClassWithoutWriteReplace1 :
+                                     Warnings.SerializableValueClassWithoutWriteReplace2);
+                 }
+             }
              return null;
          }
  
          boolean canBeSerialized(Type type) {
              return type.isPrimitive() || rs.isSerializable(type);
          }
  
+         private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
+             while (c.getKind() == ElementKind.CLASS) {
+                 Type sup = ((ClassSymbol)c).getSuperclass();
+                 if (!sup.hasTag(CLASS) || sup.isErroneous() ||
+                         sup.tsym == syms.objectType.tsym) {
+                     return false;
+                 }
+                 // if it is a value super class it has to be abstract
+                 if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
+                     return true;
+                 }
+                 c = sup.tsym;
+             }
+             return false;
+         }
+ 
          /**
           * Check that Externalizable class needs a public no-arg
           * constructor.
           *
           * Check that a Serializable class has access to the no-arg

@@ -5205,61 +5339,61 @@
              // readObject and writeObject methods and is generally
              // innocuous.
  
              // private void writeObject(ObjectOutputStream stream) throws IOException
              checkPrivateNonStaticMethod(tree, method);
-             checkReturnType(tree, e, method, syms.voidType);
+             isExpectedReturnType(tree, method, syms.voidType, true);
              checkOneArg(tree, e, method, syms.objectOutputStreamType);
-             checkExceptions(tree, e, method, syms.ioExceptionType);
+             hasExpectedExceptions(tree, method, true, syms.ioExceptionType);
              checkExternalizable(tree, e, method);
          }
  
-         private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
+         private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
              // ANY-ACCESS-MODIFIER Object writeReplace() throws
              // ObjectStreamException
  
              // Excluding abstract, could have a more complicated
              // rule based on abstract-ness of the class
-             checkConcreteInstanceMethod(tree, e, method);
-             checkReturnType(tree, e, method, syms.objectType);
-             checkNoArgs(tree, e, method);
-             checkExceptions(tree, e, method, syms.objectStreamExceptionType);
+             return isConcreteInstanceMethod(tree, method, warn) &&
+                     isExpectedReturnType(tree, method, syms.objectType, warn) &&
+                     hasNoArgs(tree, method, warn) &&
+                     hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
          }
  
          private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
              // The "synchronized" modifier is seen in the wild on
              // readObject and writeObject methods and is generally
              // innocuous.
  
              // private void readObject(ObjectInputStream stream)
              //   throws IOException, ClassNotFoundException
              checkPrivateNonStaticMethod(tree, method);
-             checkReturnType(tree, e, method, syms.voidType);
+             isExpectedReturnType(tree, method, syms.voidType, true);
              checkOneArg(tree, e, method, syms.objectInputStreamType);
-             checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
+             hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType);
              checkExternalizable(tree, e, method);
          }
  
          private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
              // private void readObjectNoData() throws ObjectStreamException
              checkPrivateNonStaticMethod(tree, method);
-             checkReturnType(tree, e, method, syms.voidType);
-             checkNoArgs(tree, e, method);
-             checkExceptions(tree, e, method, syms.objectStreamExceptionType);
+             isExpectedReturnType(tree, method, syms.voidType, true);
+             hasNoArgs(tree, method, true);
+             hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
              checkExternalizable(tree, e, method);
          }
  
          private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
              // ANY-ACCESS-MODIFIER Object readResolve()
              // throws ObjectStreamException
  
              // Excluding abstract, could have a more complicated
              // rule based on abstract-ness of the class
-             checkConcreteInstanceMethod(tree, e, method);
-             checkReturnType(tree,e, method, syms.objectType);
-             checkNoArgs(tree, e, method);
-             checkExceptions(tree, e, method, syms.objectStreamExceptionType);
+             isConcreteInstanceMethod(tree, method, true);
+             isExpectedReturnType(tree, method, syms.objectType, true);
+             hasNoArgs(tree, method, true);
+             hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
          }
  
          private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
              //public void writeExternal(ObjectOutput) throws IOException
              checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);

@@ -5505,11 +5639,11 @@
                      }
  
                      case METHOD -> {
                          var method = (MethodSymbol)enclosed;
                          switch(name) {
-                         case "writeReplace" -> checkWriteReplace(tree, e, method);
+                         case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
                          case "readResolve"  -> checkReadResolve(tree, e, method);
  
                          case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
                          case "readExternal"  -> checkReadExternalRecord(tree, e, method, isExtern);
  

@@ -5523,36 +5657,44 @@
                      }}});
              }
              return null;
          }
  
-         void checkConcreteInstanceMethod(JCClassDecl tree,
-                                          Element enclosing,
-                                          MethodSymbol method) {
+         boolean isConcreteInstanceMethod(JCClassDecl tree,
+                                          MethodSymbol method,
+                                          boolean warn) {
              if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
+                 if (warn) {
                      log.warning(LintCategory.SERIAL,
-                                 TreeInfo.diagnosticPositionFor(method, tree),
-                                 Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
+                             TreeInfo.diagnosticPositionFor(method, tree),
+                             Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
+                 }
+                 return false;
              }
+             return true;
          }
  
-         private void checkReturnType(JCClassDecl tree,
-                                      Element enclosing,
-                                      MethodSymbol method,
-                                      Type expectedReturnType) {
+         private boolean isExpectedReturnType(JCClassDecl tree,
+                                           MethodSymbol method,
+                                           Type expectedReturnType,
+                                           boolean warn) {
              // Note: there may be complications checking writeReplace
              // and readResolve since they return Object and could, in
              // principle, have covariant overrides and any synthetic
              // bridge method would not be represented here for
              // checking.
              Type rtype = method.getReturnType();
              if (!types.isSameType(expectedReturnType, rtype)) {
-                 log.warning(LintCategory.SERIAL,
+                 if (warn) {
+                     log.warning(LintCategory.SERIAL,
                              TreeInfo.diagnosticPositionFor(method, tree),
                              Warnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
-                                                                       rtype, expectedReturnType));
+                                     rtype, expectedReturnType));
+                 }
+                 return false;
              }
+             return true;
          }
  
          private void checkOneArg(JCClassDecl tree,
                                   Element enclosing,
                                   MethodSymbol method,

@@ -5586,17 +5728,21 @@
              return (parameters.size() == 1) &&
                  types.isSameType(parameters.get(0).asType(), expectedType);
          }
  
  
-         private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
+         boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
              var parameters = method.getParameters();
              if (!parameters.isEmpty()) {
-                 log.warning(LintCategory.SERIAL,
+                 if (warn) {
+                     log.warning(LintCategory.SERIAL,
                              TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
                              Warnings.SerialMethodNoArgs(method.getSimpleName()));
+                 }
+                 return false;
              }
+             return true;
          }
  
          private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
              // If the enclosing class is externalizable, warn for the method
              if (isExternalizable((Type)enclosing.asType())) {

@@ -5605,14 +5751,14 @@
                              Warnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
              }
              return;
          }
  
-         private void checkExceptions(JCClassDecl tree,
-                                      Element enclosing,
-                                      MethodSymbol method,
-                                      Type... declaredExceptions) {
+         private boolean hasExpectedExceptions(JCClassDecl tree,
+                                               MethodSymbol method,
+                                               boolean warn,
+                                               Type... declaredExceptions) {
              for (Type thrownType: method.getThrownTypes()) {
                  // For each exception in the throws clause of the
                  // method, if not an Error and not a RuntimeException,
                  // check if the exception is a subtype of a declared
                  // exception from the throws clause of the

@@ -5627,18 +5773,21 @@
                              declared = true;
                              continue;
                          }
                      }
                      if (!declared) {
-                         log.warning(LintCategory.SERIAL,
+                         if (warn) {
+                             log.warning(LintCategory.SERIAL,
                                      TreeInfo.diagnosticPositionFor(method, tree),
                                      Warnings.SerialMethodUnexpectedException(method.getSimpleName(),
-                                                                              thrownType));
+                                             thrownType));
+                         }
+                         return false;
                      }
                  }
              }
-             return;
+             return true;
          }
  
          private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
              Lint prevLint = lint;
              try {
< prev index next >