162 profile = Profile.instance(context);
163 preview = Preview.instance(context);
164
165 boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION);
166 boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL);
167 boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED);
168 boolean enforceMandatoryWarnings = true;
169
170 deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated,
171 enforceMandatoryWarnings, LintCategory.DEPRECATION, "deprecated");
172 removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval,
173 enforceMandatoryWarnings, LintCategory.REMOVAL);
174 uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked,
175 enforceMandatoryWarnings, LintCategory.UNCHECKED);
176
177 deferredLintHandler = DeferredLintHandler.instance(context);
178
179 allowModules = Feature.MODULES.allowedInSource(source);
180 allowRecords = Feature.RECORDS.allowedInSource(source);
181 allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
182 }
183
184 /** Character for synthetic names
185 */
186 char syntheticNameChar;
187
188 /** A table mapping flat names of all compiled classes for each module in this run
189 * to their symbols; maintained from outside.
190 */
191 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
192
193 /** A handler for messages about deprecated usage.
194 */
195 private MandatoryWarningHandler deprecationHandler;
196
197 /** A handler for messages about deprecated-for-removal usage.
198 */
199 private MandatoryWarningHandler removalHandler;
200
201 /** A handler for messages about unchecked or unsafe usage.
202 */
203 private MandatoryWarningHandler uncheckedHandler;
204
205 /** A handler for deferred lint warnings.
206 */
207 private DeferredLintHandler deferredLintHandler;
208
209 /** Are modules allowed
210 */
211 private final boolean allowModules;
212
213 /** Are records allowed
214 */
215 private final boolean allowRecords;
216
217 /** Are sealed classes allowed
218 */
219 private final boolean allowSealed;
220
221 /* *************************************************************************
222 * Errors and Warnings
223 **************************************************************************/
224
225 Lint setLint(Lint newLint) {
226 Lint prev = lint;
227 lint = newLint;
228 return prev;
229 }
230
231 MethodSymbol setMethod(MethodSymbol newMethod) {
232 MethodSymbol prev = method;
233 method = newMethod;
234 return prev;
235 }
236
237 /** Warn about deprecated symbol.
238 * @param pos Position to be used for error reporting.
239 * @param sym The deprecated symbol.
240 */
708 /** Check that type is a class or interface type.
709 * @param pos Position to be used for error reporting.
710 * @param t The type to be checked.
711 */
712 Type checkClassType(DiagnosticPosition pos, Type t) {
713 if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
714 return typeTagError(pos,
715 diags.fragment(Fragments.TypeReqClass),
716 asTypeParam(t));
717 } else {
718 return t;
719 }
720 }
721 //where
722 private Object asTypeParam(Type t) {
723 return (t.hasTag(TYPEVAR))
724 ? diags.fragment(Fragments.TypeParameter(t))
725 : t;
726 }
727
728 /** Check that type is a valid qualifier for a constructor reference expression
729 */
730 Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
731 t = checkClassOrArrayType(pos, t);
732 if (t.hasTag(CLASS)) {
733 if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
734 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
735 t = types.createErrorType(t);
736 } else if ((t.tsym.flags() & ENUM) != 0) {
737 log.error(pos, Errors.EnumCantBeInstantiated);
738 t = types.createErrorType(t);
739 } else {
740 t = checkClassType(pos, t, true);
741 }
742 } else if (t.hasTag(ARRAY)) {
743 if (!types.isReifiable(((ArrayType)t).elemtype)) {
744 log.error(pos, Errors.GenericArrayCreation);
745 t = types.createErrorType(t);
746 }
747 }
765 args = args.tail;
766 }
767 }
768 return t;
769 }
770
771 /** Check that type is a reference type, i.e. a class, interface or array type
772 * or a type variable.
773 * @param pos Position to be used for error reporting.
774 * @param t The type to be checked.
775 */
776 Type checkRefType(DiagnosticPosition pos, Type t) {
777 if (t.isReference())
778 return t;
779 else
780 return typeTagError(pos,
781 diags.fragment(Fragments.TypeReqRef),
782 t);
783 }
784
785 /** Check that each type is a reference type, i.e. a class, interface or array type
786 * or a type variable.
787 * @param trees Original trees, used for error reporting.
788 * @param types The types to be checked.
789 */
790 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
791 List<JCExpression> tl = trees;
792 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
793 l.head = checkRefType(tl.head.pos(), l.head);
794 tl = tl.tail;
795 }
796 return types;
797 }
798
799 /** Check that type is a null or reference type.
800 * @param pos Position to be used for error reporting.
801 * @param t The type to be checked.
802 */
803 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
804 if (t.isReference() || t.hasTag(BOT))
1156 * Warning: we can't use flags() here since this method
1157 * is called during class enter, when flags() would cause a premature
1158 * completion.
1159 * @param flags The set of modifiers given in a definition.
1160 * @param sym The defined symbol.
1161 * @param tree The declaration
1162 */
1163 long checkFlags(long flags, Symbol sym, JCTree tree) {
1164 final DiagnosticPosition pos = tree.pos();
1165 long mask;
1166 long implicit = 0;
1167
1168 switch (sym.kind) {
1169 case VAR:
1170 if (TreeInfo.isReceiverParam(tree))
1171 mask = ReceiverParamFlags;
1172 else if (sym.owner.kind != TYP)
1173 mask = LocalVarFlags;
1174 else if ((sym.owner.flags_field & INTERFACE) != 0)
1175 mask = implicit = InterfaceVarFlags;
1176 else
1177 mask = VarFlags;
1178 break;
1179 case MTH:
1180 if (sym.name == names.init) {
1181 if ((sym.owner.flags_field & ENUM) != 0) {
1182 // enum constructors cannot be declared public or
1183 // protected and must be implicitly or explicitly
1184 // private
1185 implicit = PRIVATE;
1186 mask = PRIVATE;
1187 } else
1188 mask = ConstructorFlags;
1189 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1190 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1191 mask = AnnotationTypeElementMask;
1192 implicit = PUBLIC | ABSTRACT;
1193 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1194 mask = InterfaceMethodMask;
1195 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1196 if ((flags & DEFAULT) != 0) {
1197 implicit |= ABSTRACT;
1198 }
1199 } else {
1200 mask = implicit = InterfaceMethodFlags;
1201 }
1202 } else if ((sym.owner.flags_field & RECORD) != 0) {
1203 mask = RecordMethodFlags;
1204 } else {
1205 mask = MethodFlags;
1206 }
1207 if ((flags & STRICTFP) != 0) {
1208 warnOnExplicitStrictfp(tree);
1209 }
1210 // Imply STRICTFP if owner has STRICTFP set.
1211 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1212 ((flags) & Flags.DEFAULT) != 0)
1213 implicit |= sym.owner.flags_field & STRICTFP;
1214 break;
1215 case TYP:
1216 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1217 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1218 boolean implicitlyStatic = !sym.isAnonymous() &&
1219 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1220 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1221 // local statics are allowed only if records are allowed too
1222 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
1223 implicit = implicitlyStatic ? STATIC : implicit;
1224 } else if (sym.owner.kind == TYP) {
1225 // statics in inner classes are allowed only if records are allowed too
1226 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1227 if (sym.owner.owner.kind == PCK ||
1228 (sym.owner.flags_field & STATIC) != 0) {
1229 mask |= STATIC;
1230 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1231 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1232 }
1233 // Nested interfaces and enums are always STATIC (Spec ???)
1234 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1235 } else {
1236 mask = ExtendedClassFlags;
1237 }
1238 // Interfaces are always ABSTRACT
1239 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1240
1241 if ((flags & ENUM) != 0) {
1242 // enums can't be declared abstract, final, sealed or non-sealed
1243 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
1244 implicit |= implicitEnumFinalFlag(tree);
1245 }
1246 if ((flags & RECORD) != 0) {
1247 // records can't be declared abstract
1248 mask &= ~ABSTRACT;
1249 implicit |= FINAL;
1250 }
1251 if ((flags & STRICTFP) != 0) {
1252 warnOnExplicitStrictfp(tree);
1253 }
1254 // Imply STRICTFP if owner has STRICTFP set.
1255 implicit |= sym.owner.flags_field & STRICTFP;
1256 break;
1257 default:
1258 throw new AssertionError();
1259 }
1260 long illegal = flags & ExtendedStandardFlags & ~mask;
1261 if (illegal != 0) {
1262 if ((illegal & INTERFACE) != 0) {
1263 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1264 mask |= INTERFACE;
1265 }
1266 else {
1267 log.error(pos,
1268 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1269 }
1270 }
1271 else if ((sym.kind == TYP ||
1272 // ISSUE: Disallowing abstract&private is no longer appropriate
1273 // in the presence of inner classes. Should it be deleted here?
1274 checkDisjoint(pos, flags,
1275 ABSTRACT,
1276 PRIVATE | STATIC | DEFAULT))
1277 &&
1278 checkDisjoint(pos, flags,
1279 STATIC | PRIVATE,
1280 DEFAULT)
1281 &&
1282 checkDisjoint(pos, flags,
1283 ABSTRACT | INTERFACE,
1284 FINAL | NATIVE | SYNCHRONIZED)
1285 &&
1286 checkDisjoint(pos, flags,
1287 PUBLIC,
1288 PRIVATE | PROTECTED)
1289 &&
1290 checkDisjoint(pos, flags,
1291 PRIVATE,
1292 PUBLIC | PROTECTED)
1293 &&
1294 checkDisjoint(pos, flags,
1295 FINAL,
1296 VOLATILE)
1297 &&
1298 (sym.kind == TYP ||
1299 checkDisjoint(pos, flags,
1300 ABSTRACT | NATIVE,
1301 STRICTFP))
1302 && checkDisjoint(pos, flags,
1303 FINAL,
1304 SEALED | NON_SEALED)
1305 && checkDisjoint(pos, flags,
1306 SEALED,
1307 FINAL | NON_SEALED)
1308 && checkDisjoint(pos, flags,
1309 SEALED,
1310 ANNOTATION)) {
1311 // skip
1312 }
1313 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1314 }
1315
1316 private void warnOnExplicitStrictfp(JCTree tree) {
1317 deferredLintHandler.push(tree);
1318 try {
1319 deferredLintHandler.report(_ -> lint.logIfEnabled(tree.pos(), LintWarnings.Strictfp));
1320 } finally {
1321 deferredLintHandler.pop();
1322 }
1323 }
1324
1325
1326 /** Determine if this enum should be implicitly final.
1327 *
1328 * If the enum has no specialized enum constants, it is final.
1329 *
1330 * If the enum does have specialized enum constants, it is
2102 return true;
2103 }
2104 }
2105 }
2106 return false;
2107 }
2108
2109 /** Check that a given method conforms with any method it overrides.
2110 * @param tree The tree from which positions are extracted
2111 * for errors.
2112 * @param m The overriding method.
2113 */
2114 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2115 ClassSymbol origin = (ClassSymbol)m.owner;
2116 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2117 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2118 log.error(tree.pos(), Errors.EnumNoFinalize);
2119 return;
2120 }
2121 }
2122 if (allowRecords && origin.isRecord()) {
2123 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2124 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2125 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2126 if (recordComponent.isPresent()) {
2127 return;
2128 }
2129 }
2130
2131 for (Type t = origin.type; t.hasTag(CLASS);
2132 t = types.supertype(t)) {
2133 if (t != origin.type) {
2134 checkOverride(tree, t, origin, m);
2135 }
2136 for (Type t2 : types.interfaces(t)) {
2137 checkOverride(tree, t2, origin, m);
2138 }
2139 }
2140
2141 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2537 /** Check that all abstract methods implemented by a class are
2538 * mutually compatible.
2539 * @param pos Position to be used for error reporting.
2540 * @param c The class whose interfaces are checked.
2541 */
2542 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2543 List<Type> supertypes = types.interfaces(c);
2544 Type supertype = types.supertype(c);
2545 if (supertype.hasTag(CLASS) &&
2546 (supertype.tsym.flags() & ABSTRACT) != 0)
2547 supertypes = supertypes.prepend(supertype);
2548 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2549 if (!l.head.getTypeArguments().isEmpty() &&
2550 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2551 return;
2552 for (List<Type> m = supertypes; m != l; m = m.tail)
2553 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2554 return;
2555 }
2556 checkCompatibleConcretes(pos, c);
2557 }
2558
2559 /** Check that all non-override equivalent methods accessible from 'site'
2560 * are mutually compatible (JLS 8.4.8/9.4.1).
2561 *
2562 * @param pos Position to be used for error reporting.
2563 * @param site The class whose methods are checked.
2564 * @param sym The method symbol to be checked.
2565 */
2566 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2567 ClashFilter cf = new ClashFilter(site);
2568 //for each method m1 that is overridden (directly or indirectly)
2569 //by method 'sym' in 'site'...
2570
2571 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2572 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2573 for (Symbol m1 : symbolsByName) {
2574 if (!sym.overrides(m1, site.tsym, types, false)) {
2575 continue;
2576 }
4889 }
4890 } else {
4891 Assert.error("Unknown pattern: " + currentPattern.getTag());
4892 }
4893 return false;
4894 }
4895
4896 /** check if a type is a subtype of Externalizable, if that is available. */
4897 boolean isExternalizable(Type t) {
4898 try {
4899 syms.externalizableType.complete();
4900 } catch (CompletionFailure e) {
4901 return false;
4902 }
4903 return types.isSubtype(t, syms.externalizableType);
4904 }
4905
4906 /**
4907 * Check structure of serialization declarations.
4908 */
4909 public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
4910 (new SerialTypeVisitor()).visit(c, tree);
4911 }
4912
4913 /**
4914 * This visitor will warn if a serialization-related field or
4915 * method is declared in a suspicious or incorrect way. In
4916 * particular, it will warn for cases where the runtime
4917 * serialization mechanism will silently ignore a mis-declared
4918 * entity.
4919 *
4920 * Distinguished serialization-related fields and methods:
4921 *
4922 * Methods:
4923 *
4924 * private void writeObject(ObjectOutputStream stream) throws IOException
4925 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4926 *
4927 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4928 * private void readObjectNoData() throws ObjectStreamException
4929 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4930 *
4931 * Fields:
4932 *
4933 * private static final long serialVersionUID
4934 * private static final ObjectStreamField[] serialPersistentFields
4935 *
4936 * Externalizable: methods defined on the interface
4937 * public void writeExternal(ObjectOutput) throws IOException
4938 * public void readExternal(ObjectInput) throws IOException
4939 */
4940 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4941 SerialTypeVisitor() {
4942 this.lint = Check.this.lint;
4943 }
4944
4945 private static final Set<String> serialMethodNames =
4946 Set.of("writeObject", "writeReplace",
4947 "readObject", "readObjectNoData",
4948 "readResolve");
4949
4950 private static final Set<String> serialFieldNames =
4951 Set.of("serialVersionUID", "serialPersistentFields");
4952
4953 // Type of serialPersistentFields
4954 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
4955
4956 Lint lint;
4957
4958 @Override
4959 public Void defaultAction(Element e, JCClassDecl p) {
4960 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
4961 }
4962
4982 if (sym.kind == VAR) {
4983 svuidSym = (VarSymbol)sym;
4984 break;
4985 }
4986 }
4987
4988 if (svuidSym == null) {
4989 log.warning(p.pos(), LintWarnings.MissingSVUID(c));
4990 }
4991
4992 // Check for serialPersistentFields to gate checks for
4993 // non-serializable non-transient instance fields
4994 boolean serialPersistentFieldsPresent =
4995 c.members()
4996 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
4997 .iterator()
4998 .hasNext();
4999
5000 // Check declarations of serialization-related methods and
5001 // fields
5002 for(Symbol el : c.getEnclosedElements()) {
5003 runUnderLint(el, p, (enclosed, tree) -> {
5004 String name = null;
5005 switch(enclosed.getKind()) {
5006 case FIELD -> {
5007 if (!serialPersistentFieldsPresent) {
5008 var flags = enclosed.flags();
5009 if ( ((flags & TRANSIENT) == 0) &&
5010 ((flags & STATIC) == 0)) {
5011 Type varType = enclosed.asType();
5012 if (!canBeSerialized(varType)) {
5013 // Note per JLS arrays are
5014 // serializable even if the
5015 // component type is not.
5016 log.warning(
5017 TreeInfo.diagnosticPositionFor(enclosed, tree),
5018 LintWarnings.NonSerializableInstanceField);
5019 } else if (varType.hasTag(ARRAY)) {
5020 ArrayType arrayType = (ArrayType)varType;
5021 Type elementType = arrayType.elemtype;
5056 // will also pull in default methods from
5057 // superinterfaces. In other words, the runtime checks
5058 // (which long predate default methods on interfaces)
5059 // do not admit the possibility of inheriting methods
5060 // this way, a difference from general inheritance.
5061
5062 // The current implementation just checks the enclosed
5063 // elements and does not directly check the inherited
5064 // methods. If all the types are being checked this is
5065 // less of a concern; however, there are cases that
5066 // could be missed. In particular, readResolve and
5067 // writeReplace could, in principle, by inherited from
5068 // a non-serializable superclass and thus not checked
5069 // even if compiled with a serializable child class.
5070 case METHOD -> {
5071 var method = (MethodSymbol)enclosed;
5072 name = method.getSimpleName().toString();
5073 if (serialMethodNames.contains(name)) {
5074 switch (name) {
5075 case "writeObject" -> checkWriteObject(tree, e, method);
5076 case "writeReplace" -> checkWriteReplace(tree,e, method);
5077 case "readObject" -> checkReadObject(tree,e, method);
5078 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5079 case "readResolve" -> checkReadResolve(tree, e, method);
5080 default -> throw new AssertionError();
5081 }
5082 }
5083 }
5084 }
5085 });
5086 }
5087
5088 return null;
5089 }
5090
5091 boolean canBeSerialized(Type type) {
5092 return type.isPrimitive() || rs.isSerializable(type);
5093 }
5094
5095 /**
5096 * Check that Externalizable class needs a public no-arg
5097 * constructor.
5098 *
5099 * Check that a Serializable class has access to the no-arg
5100 * constructor of its first nonserializable superclass.
5101 */
5102 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5103 if (isExternalizable(c.type)) {
5104 for(var sym : c.getEnclosedElements()) {
5105 if (sym.isConstructor() &&
5106 ((sym.flags() & PUBLIC) == PUBLIC)) {
5107 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5108 return;
5109 }
5110 }
5111 }
5112 log.warning(tree.pos(),
5113 LintWarnings.ExternalizableMissingPublicNoArgCtor);
5114 } else {
5198 // Warn if serialPersistentFields is initialized to a
5199 // literal null.
5200 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5201 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5202 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5203 JCExpression initExpr = variableDef.init;
5204 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5205 log.warning(initExpr.pos(),
5206 LintWarnings.SPFNullInit);
5207 }
5208 }
5209 }
5210
5211 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5212 // The "synchronized" modifier is seen in the wild on
5213 // readObject and writeObject methods and is generally
5214 // innocuous.
5215
5216 // private void writeObject(ObjectOutputStream stream) throws IOException
5217 checkPrivateNonStaticMethod(tree, method);
5218 checkReturnType(tree, e, method, syms.voidType);
5219 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5220 checkExceptions(tree, e, method, syms.ioExceptionType);
5221 checkExternalizable(tree, e, method);
5222 }
5223
5224 private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
5225 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5226 // ObjectStreamException
5227
5228 // Excluding abstract, could have a more complicated
5229 // rule based on abstract-ness of the class
5230 checkConcreteInstanceMethod(tree, e, method);
5231 checkReturnType(tree, e, method, syms.objectType);
5232 checkNoArgs(tree, e, method);
5233 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5234 }
5235
5236 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5237 // The "synchronized" modifier is seen in the wild on
5238 // readObject and writeObject methods and is generally
5239 // innocuous.
5240
5241 // private void readObject(ObjectInputStream stream)
5242 // throws IOException, ClassNotFoundException
5243 checkPrivateNonStaticMethod(tree, method);
5244 checkReturnType(tree, e, method, syms.voidType);
5245 checkOneArg(tree, e, method, syms.objectInputStreamType);
5246 checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
5247 checkExternalizable(tree, e, method);
5248 }
5249
5250 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5251 // private void readObjectNoData() throws ObjectStreamException
5252 checkPrivateNonStaticMethod(tree, method);
5253 checkReturnType(tree, e, method, syms.voidType);
5254 checkNoArgs(tree, e, method);
5255 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5256 checkExternalizable(tree, e, method);
5257 }
5258
5259 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5260 // ANY-ACCESS-MODIFIER Object readResolve()
5261 // throws ObjectStreamException
5262
5263 // Excluding abstract, could have a more complicated
5264 // rule based on abstract-ness of the class
5265 checkConcreteInstanceMethod(tree, e, method);
5266 checkReturnType(tree,e, method, syms.objectType);
5267 checkNoArgs(tree, e, method);
5268 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5269 }
5270
5271 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5272 //public void writeExternal(ObjectOutput) throws IOException
5273 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5274 }
5275
5276 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5277 // public void readExternal(ObjectInput) throws IOException
5278 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5279 }
5280
5281 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5282 boolean isExtern) {
5283 if (isExtern && isExternMethod(tree, e, method, argType)) {
5284 log.warning(
5285 TreeInfo.diagnosticPositionFor(method, tree),
5286 LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5287 }
5288 }
5498 case FIELD -> {
5499 var field = (VarSymbol)enclosed;
5500 switch(name) {
5501 case "serialPersistentFields" -> {
5502 log.warning(
5503 TreeInfo.diagnosticPositionFor(field, tree),
5504 LintWarnings.IneffectualSerialFieldRecord);
5505 }
5506
5507 case "serialVersionUID" -> {
5508 // Could generate additional warning that
5509 // svuid value is not checked to match for
5510 // records.
5511 checkSerialVersionUID(tree, e, field);
5512 }}
5513 }
5514
5515 case METHOD -> {
5516 var method = (MethodSymbol)enclosed;
5517 switch(name) {
5518 case "writeReplace" -> checkWriteReplace(tree, e, method);
5519 case "readResolve" -> checkReadResolve(tree, e, method);
5520
5521 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5522 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5523
5524 default -> {
5525 if (serialMethodNames.contains(name)) {
5526 log.warning(
5527 TreeInfo.diagnosticPositionFor(method, tree),
5528 LintWarnings.IneffectualSerialMethodRecord(name));
5529 }
5530 }}
5531 }}});
5532 }
5533 return null;
5534 }
5535
5536 void checkConcreteInstanceMethod(JCClassDecl tree,
5537 Element enclosing,
5538 MethodSymbol method) {
5539 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5540 log.warning(
5541 TreeInfo.diagnosticPositionFor(method, tree),
5542 LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5543 }
5544 }
5545
5546 private void checkReturnType(JCClassDecl tree,
5547 Element enclosing,
5548 MethodSymbol method,
5549 Type expectedReturnType) {
5550 // Note: there may be complications checking writeReplace
5551 // and readResolve since they return Object and could, in
5552 // principle, have covariant overrides and any synthetic
5553 // bridge method would not be represented here for
5554 // checking.
5555 Type rtype = method.getReturnType();
5556 if (!types.isSameType(expectedReturnType, rtype)) {
5557 log.warning(
5558 TreeInfo.diagnosticPositionFor(method, tree),
5559 LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5560 rtype, expectedReturnType));
5561 }
5562 }
5563
5564 private void checkOneArg(JCClassDecl tree,
5565 Element enclosing,
5566 MethodSymbol method,
5567 Type expectedType) {
5568 String name = method.getSimpleName().toString();
5569
5570 var parameters= method.getParameters();
5571
5572 if (parameters.size() != 1) {
5573 log.warning(
5574 TreeInfo.diagnosticPositionFor(method, tree),
5575 LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5576 return;
5577 }
5578
5579 Type parameterType = parameters.get(0).asType();
5580 if (!types.isSameType(parameterType, expectedType)) {
5581 log.warning(
5582 TreeInfo.diagnosticPositionFor(method, tree),
5583 LintWarnings.SerialMethodParameterType(method.getSimpleName(),
5584 expectedType,
5585 parameterType));
5586 }
5587 }
5588
5589 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5590 Element enclosing,
5591 MethodSymbol method,
5592 Type expectedType) {
5593 var parameters = method.getParameters();
5594 return (parameters.size() == 1) &&
5595 types.isSameType(parameters.get(0).asType(), expectedType);
5596 }
5597
5598
5599 private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5600 var parameters = method.getParameters();
5601 if (!parameters.isEmpty()) {
5602 log.warning(
5603 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5604 LintWarnings.SerialMethodNoArgs(method.getSimpleName()));
5605 }
5606 }
5607
5608 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5609 // If the enclosing class is externalizable, warn for the method
5610 if (isExternalizable((Type)enclosing.asType())) {
5611 log.warning(
5612 TreeInfo.diagnosticPositionFor(method, tree),
5613 LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5614 }
5615 return;
5616 }
5617
5618 private void checkExceptions(JCClassDecl tree,
5619 Element enclosing,
5620 MethodSymbol method,
5621 Type... declaredExceptions) {
5622 for (Type thrownType: method.getThrownTypes()) {
5623 // For each exception in the throws clause of the
5624 // method, if not an Error and not a RuntimeException,
5625 // check if the exception is a subtype of a declared
5626 // exception from the throws clause of the
5627 // serialization method in question.
5628 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5629 types.isSubtype(thrownType, syms.errorType) ) {
5630 continue;
5631 } else {
5632 boolean declared = false;
5633 for (Type declaredException : declaredExceptions) {
5634 if (types.isSubtype(thrownType, declaredException)) {
5635 declared = true;
5636 continue;
5637 }
5638 }
5639 if (!declared) {
5640 log.warning(
5641 TreeInfo.diagnosticPositionFor(method, tree),
5642 LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(),
5643 thrownType));
5644 }
5645 }
5646 }
5647 return;
5648 }
5649
5650 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5651 Lint prevLint = lint;
5652 try {
5653 lint = lint.augment((Symbol) symbol);
5654
5655 if (lint.isEnabled(LintCategory.SERIAL)) {
5656 task.accept(symbol, p);
5657 }
5658
5659 return null;
5660 } finally {
5661 lint = prevLint;
5662 }
5663 }
5664
5665 }
5666
5667 }
|
162 profile = Profile.instance(context);
163 preview = Preview.instance(context);
164
165 boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION);
166 boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL);
167 boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED);
168 boolean enforceMandatoryWarnings = true;
169
170 deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated,
171 enforceMandatoryWarnings, LintCategory.DEPRECATION, "deprecated");
172 removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval,
173 enforceMandatoryWarnings, LintCategory.REMOVAL);
174 uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked,
175 enforceMandatoryWarnings, LintCategory.UNCHECKED);
176
177 deferredLintHandler = DeferredLintHandler.instance(context);
178
179 allowModules = Feature.MODULES.allowedInSource(source);
180 allowRecords = Feature.RECORDS.allowedInSource(source);
181 allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
182 allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
183 Feature.VALUE_CLASSES.allowedInSource(source);
184 }
185
186 /** Character for synthetic names
187 */
188 char syntheticNameChar;
189
190 /** A table mapping flat names of all compiled classes for each module in this run
191 * to their symbols; maintained from outside.
192 */
193 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
194
195 /** A handler for messages about deprecated usage.
196 */
197 private MandatoryWarningHandler deprecationHandler;
198
199 /** A handler for messages about deprecated-for-removal usage.
200 */
201 private MandatoryWarningHandler removalHandler;
202
203 /** A handler for messages about unchecked or unsafe usage.
204 */
205 private MandatoryWarningHandler uncheckedHandler;
206
207 /** A handler for deferred lint warnings.
208 */
209 private DeferredLintHandler deferredLintHandler;
210
211 /** Are modules allowed
212 */
213 private final boolean allowModules;
214
215 /** Are records allowed
216 */
217 private final boolean allowRecords;
218
219 /** Are sealed classes allowed
220 */
221 private final boolean allowSealed;
222
223 /** Are value classes allowed
224 */
225 private final boolean allowValueClasses;
226
227 /* *************************************************************************
228 * Errors and Warnings
229 **************************************************************************/
230
231 Lint setLint(Lint newLint) {
232 Lint prev = lint;
233 lint = newLint;
234 return prev;
235 }
236
237 MethodSymbol setMethod(MethodSymbol newMethod) {
238 MethodSymbol prev = method;
239 method = newMethod;
240 return prev;
241 }
242
243 /** Warn about deprecated symbol.
244 * @param pos Position to be used for error reporting.
245 * @param sym The deprecated symbol.
246 */
714 /** Check that type is a class or interface type.
715 * @param pos Position to be used for error reporting.
716 * @param t The type to be checked.
717 */
718 Type checkClassType(DiagnosticPosition pos, Type t) {
719 if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
720 return typeTagError(pos,
721 diags.fragment(Fragments.TypeReqClass),
722 asTypeParam(t));
723 } else {
724 return t;
725 }
726 }
727 //where
728 private Object asTypeParam(Type t) {
729 return (t.hasTag(TYPEVAR))
730 ? diags.fragment(Fragments.TypeParameter(t))
731 : t;
732 }
733
734 void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
735 DiagnosticPosition pos = tree.pos();
736 for (Type st : types.closure(c.type)) {
737 if (st == null || st.tsym == null || st.tsym.kind == ERR)
738 continue;
739 if (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
740 continue;
741 if (!st.tsym.isAbstract()) {
742 if (c != st.tsym) {
743 log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
744 }
745 continue;
746 }
747 // dealing with an abstract value or value super class below.
748 for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
749 if (s.kind == MTH) {
750 if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
751 log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
752 }
753 break;
754 }
755 }
756 }
757 }
758
759 /** Check that type is a valid qualifier for a constructor reference expression
760 */
761 Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
762 t = checkClassOrArrayType(pos, t);
763 if (t.hasTag(CLASS)) {
764 if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
765 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
766 t = types.createErrorType(t);
767 } else if ((t.tsym.flags() & ENUM) != 0) {
768 log.error(pos, Errors.EnumCantBeInstantiated);
769 t = types.createErrorType(t);
770 } else {
771 t = checkClassType(pos, t, true);
772 }
773 } else if (t.hasTag(ARRAY)) {
774 if (!types.isReifiable(((ArrayType)t).elemtype)) {
775 log.error(pos, Errors.GenericArrayCreation);
776 t = types.createErrorType(t);
777 }
778 }
796 args = args.tail;
797 }
798 }
799 return t;
800 }
801
802 /** Check that type is a reference type, i.e. a class, interface or array type
803 * or a type variable.
804 * @param pos Position to be used for error reporting.
805 * @param t The type to be checked.
806 */
807 Type checkRefType(DiagnosticPosition pos, Type t) {
808 if (t.isReference())
809 return t;
810 else
811 return typeTagError(pos,
812 diags.fragment(Fragments.TypeReqRef),
813 t);
814 }
815
816 /** Check that type is an identity type, i.e. not a value type.
817 * When not discernible statically, give it the benefit of doubt
818 * and defer to runtime.
819 *
820 * @param pos Position to be used for error reporting.
821 * @param t The type to be checked.
822 */
823 boolean checkIdentityType(DiagnosticPosition pos, Type t) {
824 if (t.hasTag(TYPEVAR)) {
825 t = types.skipTypeVars(t, false);
826 }
827 if (t.isIntersection()) {
828 IntersectionClassType ict = (IntersectionClassType)t;
829 boolean result = true;
830 for (Type component : ict.getExplicitComponents()) {
831 result &= checkIdentityType(pos, component);
832 }
833 return result;
834 }
835 if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
836 typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
837 return false;
838 }
839 return true;
840 }
841
842 /** Check that each type is a reference type, i.e. a class, interface or array type
843 * or a type variable.
844 * @param trees Original trees, used for error reporting.
845 * @param types The types to be checked.
846 */
847 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
848 List<JCExpression> tl = trees;
849 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
850 l.head = checkRefType(tl.head.pos(), l.head);
851 tl = tl.tail;
852 }
853 return types;
854 }
855
856 /** Check that type is a null or reference type.
857 * @param pos Position to be used for error reporting.
858 * @param t The type to be checked.
859 */
860 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
861 if (t.isReference() || t.hasTag(BOT))
1213 * Warning: we can't use flags() here since this method
1214 * is called during class enter, when flags() would cause a premature
1215 * completion.
1216 * @param flags The set of modifiers given in a definition.
1217 * @param sym The defined symbol.
1218 * @param tree The declaration
1219 */
1220 long checkFlags(long flags, Symbol sym, JCTree tree) {
1221 final DiagnosticPosition pos = tree.pos();
1222 long mask;
1223 long implicit = 0;
1224
1225 switch (sym.kind) {
1226 case VAR:
1227 if (TreeInfo.isReceiverParam(tree))
1228 mask = ReceiverParamFlags;
1229 else if (sym.owner.kind != TYP)
1230 mask = LocalVarFlags;
1231 else if ((sym.owner.flags_field & INTERFACE) != 0)
1232 mask = implicit = InterfaceVarFlags;
1233 else {
1234 boolean isInstanceFieldOfValueClass = sym.owner.type.isValueClass() && (flags & STATIC) == 0;
1235 mask = !isInstanceFieldOfValueClass ? VarFlags : ValueFieldFlags;
1236 if (isInstanceFieldOfValueClass) {
1237 implicit |= FINAL | STRICT;
1238 }
1239 }
1240 break;
1241 case MTH:
1242 if (sym.name == names.init) {
1243 if ((sym.owner.flags_field & ENUM) != 0) {
1244 // enum constructors cannot be declared public or
1245 // protected and must be implicitly or explicitly
1246 // private
1247 implicit = PRIVATE;
1248 mask = PRIVATE;
1249 } else
1250 mask = ConstructorFlags;
1251 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1252 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1253 mask = AnnotationTypeElementMask;
1254 implicit = PUBLIC | ABSTRACT;
1255 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1256 mask = InterfaceMethodMask;
1257 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1258 if ((flags & DEFAULT) != 0) {
1259 implicit |= ABSTRACT;
1260 }
1261 } else {
1262 mask = implicit = InterfaceMethodFlags;
1263 }
1264 } else if ((sym.owner.flags_field & RECORD) != 0) {
1265 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1266 RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
1267 } else {
1268 // value objects do not have an associated monitor/lock
1269 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1270 MethodFlags & ~SYNCHRONIZED : MethodFlags;
1271 }
1272 if ((flags & STRICTFP) != 0) {
1273 warnOnExplicitStrictfp(tree);
1274 }
1275 // Imply STRICTFP if owner has STRICTFP set.
1276 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1277 ((flags) & Flags.DEFAULT) != 0)
1278 implicit |= sym.owner.flags_field & STRICTFP;
1279 break;
1280 case TYP:
1281 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1282 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1283 boolean implicitlyStatic = !sym.isAnonymous() &&
1284 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1285 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1286 // local statics are allowed only if records are allowed too
1287 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
1288 implicit = implicitlyStatic ? STATIC : implicit;
1289 } else if (sym.owner.kind == TYP) {
1290 // statics in inner classes are allowed only if records are allowed too
1291 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1292 if (sym.owner.owner.kind == PCK ||
1293 (sym.owner.flags_field & STATIC) != 0) {
1294 mask |= STATIC;
1295 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1296 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1297 }
1298 // Nested interfaces and enums are always STATIC (Spec ???)
1299 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1300 } else {
1301 mask = ExtendedClassFlags;
1302 }
1303 if ((flags & (VALUE_CLASS | SEALED | ABSTRACT)) == (VALUE_CLASS | SEALED) ||
1304 (flags & (VALUE_CLASS | NON_SEALED | ABSTRACT)) == (VALUE_CLASS | NON_SEALED)) {
1305 log.error(pos, Errors.NonAbstractValueClassCantBeSealedOrNonSealed);
1306 }
1307 // Interfaces are always ABSTRACT
1308 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1309
1310 if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
1311 implicit |= IDENTITY_TYPE;
1312 }
1313
1314 if ((flags & ENUM) != 0) {
1315 // enums can't be declared abstract, final, sealed or non-sealed or value
1316 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
1317 implicit |= implicitEnumFinalFlag(tree);
1318 }
1319 if ((flags & RECORD) != 0) {
1320 // records can't be declared abstract
1321 mask &= ~ABSTRACT;
1322 implicit |= FINAL;
1323 }
1324 if ((flags & STRICTFP) != 0) {
1325 warnOnExplicitStrictfp(tree);
1326 }
1327 // Imply STRICTFP if owner has STRICTFP set.
1328 implicit |= sym.owner.flags_field & STRICTFP;
1329
1330 // concrete value classes are implicitly final
1331 if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
1332 implicit |= FINAL;
1333 }
1334 break;
1335 default:
1336 throw new AssertionError();
1337 }
1338 long illegal = flags & ExtendedStandardFlags & ~mask;
1339 if (illegal != 0) {
1340 if ((illegal & INTERFACE) != 0) {
1341 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1342 mask |= INTERFACE;
1343 }
1344 else {
1345 log.error(pos,
1346 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1347 }
1348 } else if ((sym.kind == TYP ||
1349 // ISSUE: Disallowing abstract&private is no longer appropriate
1350 // in the presence of inner classes. Should it be deleted here?
1351 checkDisjoint(pos, flags,
1352 ABSTRACT,
1353 PRIVATE | STATIC | DEFAULT))
1354 &&
1355 checkDisjoint(pos, flags,
1356 STATIC | PRIVATE,
1357 DEFAULT)
1358 &&
1359 checkDisjoint(pos, flags,
1360 ABSTRACT | INTERFACE,
1361 FINAL | NATIVE | SYNCHRONIZED)
1362 &&
1363 checkDisjoint(pos, flags,
1364 PUBLIC,
1365 PRIVATE | PROTECTED)
1366 &&
1367 checkDisjoint(pos, flags,
1368 PRIVATE,
1369 PUBLIC | PROTECTED)
1370 &&
1371 // we are using `implicit` here as instance fields of value classes are implicitly final
1372 checkDisjoint(pos, flags | implicit,
1373 FINAL,
1374 VOLATILE)
1375 &&
1376 (sym.kind == TYP ||
1377 checkDisjoint(pos, flags,
1378 ABSTRACT | NATIVE,
1379 STRICTFP))
1380 && checkDisjoint(pos, flags,
1381 FINAL,
1382 SEALED | NON_SEALED)
1383 && checkDisjoint(pos, flags,
1384 SEALED,
1385 FINAL | NON_SEALED)
1386 && checkDisjoint(pos, flags,
1387 SEALED,
1388 ANNOTATION)
1389 && checkDisjoint(pos, flags,
1390 VALUE_CLASS,
1391 ANNOTATION)
1392 && checkDisjoint(pos, flags,
1393 VALUE_CLASS,
1394 INTERFACE) ) {
1395 // skip
1396 }
1397 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1398 }
1399
1400 private void warnOnExplicitStrictfp(JCTree tree) {
1401 deferredLintHandler.push(tree);
1402 try {
1403 deferredLintHandler.report(_ -> lint.logIfEnabled(tree.pos(), LintWarnings.Strictfp));
1404 } finally {
1405 deferredLintHandler.pop();
1406 }
1407 }
1408
1409
1410 /** Determine if this enum should be implicitly final.
1411 *
1412 * If the enum has no specialized enum constants, it is final.
1413 *
1414 * If the enum does have specialized enum constants, it is
2186 return true;
2187 }
2188 }
2189 }
2190 return false;
2191 }
2192
2193 /** Check that a given method conforms with any method it overrides.
2194 * @param tree The tree from which positions are extracted
2195 * for errors.
2196 * @param m The overriding method.
2197 */
2198 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2199 ClassSymbol origin = (ClassSymbol)m.owner;
2200 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2201 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2202 log.error(tree.pos(), Errors.EnumNoFinalize);
2203 return;
2204 }
2205 }
2206 if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
2207 if (m.overrides(syms.objectFinalize, origin, types, false)) {
2208 log.warning(tree.pos(), Warnings.ValueFinalize);
2209 }
2210 }
2211 if (allowRecords && origin.isRecord()) {
2212 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2213 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2214 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2215 if (recordComponent.isPresent()) {
2216 return;
2217 }
2218 }
2219
2220 for (Type t = origin.type; t.hasTag(CLASS);
2221 t = types.supertype(t)) {
2222 if (t != origin.type) {
2223 checkOverride(tree, t, origin, m);
2224 }
2225 for (Type t2 : types.interfaces(t)) {
2226 checkOverride(tree, t2, origin, m);
2227 }
2228 }
2229
2230 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2626 /** Check that all abstract methods implemented by a class are
2627 * mutually compatible.
2628 * @param pos Position to be used for error reporting.
2629 * @param c The class whose interfaces are checked.
2630 */
2631 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2632 List<Type> supertypes = types.interfaces(c);
2633 Type supertype = types.supertype(c);
2634 if (supertype.hasTag(CLASS) &&
2635 (supertype.tsym.flags() & ABSTRACT) != 0)
2636 supertypes = supertypes.prepend(supertype);
2637 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2638 if (!l.head.getTypeArguments().isEmpty() &&
2639 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2640 return;
2641 for (List<Type> m = supertypes; m != l; m = m.tail)
2642 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2643 return;
2644 }
2645 checkCompatibleConcretes(pos, c);
2646
2647 Type identitySuper = null;
2648 for (Type t : types.closure(c)) {
2649 if (t != c) {
2650 if (t.isIdentityClass() && (t.tsym.flags() & VALUE_BASED) == 0)
2651 identitySuper = t;
2652 if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
2653 log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
2654 break;
2655 }
2656 }
2657 }
2658 }
2659
2660 /** Check that all non-override equivalent methods accessible from 'site'
2661 * are mutually compatible (JLS 8.4.8/9.4.1).
2662 *
2663 * @param pos Position to be used for error reporting.
2664 * @param site The class whose methods are checked.
2665 * @param sym The method symbol to be checked.
2666 */
2667 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2668 ClashFilter cf = new ClashFilter(site);
2669 //for each method m1 that is overridden (directly or indirectly)
2670 //by method 'sym' in 'site'...
2671
2672 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2673 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2674 for (Symbol m1 : symbolsByName) {
2675 if (!sym.overrides(m1, site.tsym, types, false)) {
2676 continue;
2677 }
4990 }
4991 } else {
4992 Assert.error("Unknown pattern: " + currentPattern.getTag());
4993 }
4994 return false;
4995 }
4996
4997 /** check if a type is a subtype of Externalizable, if that is available. */
4998 boolean isExternalizable(Type t) {
4999 try {
5000 syms.externalizableType.complete();
5001 } catch (CompletionFailure e) {
5002 return false;
5003 }
5004 return types.isSubtype(t, syms.externalizableType);
5005 }
5006
5007 /**
5008 * Check structure of serialization declarations.
5009 */
5010 public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
5011 (new SerialTypeVisitor(env)).visit(c, tree);
5012 }
5013
5014 /**
5015 * This visitor will warn if a serialization-related field or
5016 * method is declared in a suspicious or incorrect way. In
5017 * particular, it will warn for cases where the runtime
5018 * serialization mechanism will silently ignore a mis-declared
5019 * entity.
5020 *
5021 * Distinguished serialization-related fields and methods:
5022 *
5023 * Methods:
5024 *
5025 * private void writeObject(ObjectOutputStream stream) throws IOException
5026 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
5027 *
5028 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
5029 * private void readObjectNoData() throws ObjectStreamException
5030 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
5031 *
5032 * Fields:
5033 *
5034 * private static final long serialVersionUID
5035 * private static final ObjectStreamField[] serialPersistentFields
5036 *
5037 * Externalizable: methods defined on the interface
5038 * public void writeExternal(ObjectOutput) throws IOException
5039 * public void readExternal(ObjectInput) throws IOException
5040 */
5041 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
5042 Env<AttrContext> env;
5043 SerialTypeVisitor(Env<AttrContext> env) {
5044 this.lint = Check.this.lint;
5045 this.env = env;
5046 }
5047
5048 private static final Set<String> serialMethodNames =
5049 Set.of("writeObject", "writeReplace",
5050 "readObject", "readObjectNoData",
5051 "readResolve");
5052
5053 private static final Set<String> serialFieldNames =
5054 Set.of("serialVersionUID", "serialPersistentFields");
5055
5056 // Type of serialPersistentFields
5057 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
5058
5059 Lint lint;
5060
5061 @Override
5062 public Void defaultAction(Element e, JCClassDecl p) {
5063 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
5064 }
5065
5085 if (sym.kind == VAR) {
5086 svuidSym = (VarSymbol)sym;
5087 break;
5088 }
5089 }
5090
5091 if (svuidSym == null) {
5092 log.warning(p.pos(), LintWarnings.MissingSVUID(c));
5093 }
5094
5095 // Check for serialPersistentFields to gate checks for
5096 // non-serializable non-transient instance fields
5097 boolean serialPersistentFieldsPresent =
5098 c.members()
5099 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
5100 .iterator()
5101 .hasNext();
5102
5103 // Check declarations of serialization-related methods and
5104 // fields
5105 final boolean[] hasWriteReplace = {false};
5106 for(Symbol el : c.getEnclosedElements()) {
5107 runUnderLint(el, p, (enclosed, tree) -> {
5108 String name = null;
5109 switch(enclosed.getKind()) {
5110 case FIELD -> {
5111 if (!serialPersistentFieldsPresent) {
5112 var flags = enclosed.flags();
5113 if ( ((flags & TRANSIENT) == 0) &&
5114 ((flags & STATIC) == 0)) {
5115 Type varType = enclosed.asType();
5116 if (!canBeSerialized(varType)) {
5117 // Note per JLS arrays are
5118 // serializable even if the
5119 // component type is not.
5120 log.warning(
5121 TreeInfo.diagnosticPositionFor(enclosed, tree),
5122 LintWarnings.NonSerializableInstanceField);
5123 } else if (varType.hasTag(ARRAY)) {
5124 ArrayType arrayType = (ArrayType)varType;
5125 Type elementType = arrayType.elemtype;
5160 // will also pull in default methods from
5161 // superinterfaces. In other words, the runtime checks
5162 // (which long predate default methods on interfaces)
5163 // do not admit the possibility of inheriting methods
5164 // this way, a difference from general inheritance.
5165
5166 // The current implementation just checks the enclosed
5167 // elements and does not directly check the inherited
5168 // methods. If all the types are being checked this is
5169 // less of a concern; however, there are cases that
5170 // could be missed. In particular, readResolve and
5171 // writeReplace could, in principle, by inherited from
5172 // a non-serializable superclass and thus not checked
5173 // even if compiled with a serializable child class.
5174 case METHOD -> {
5175 var method = (MethodSymbol)enclosed;
5176 name = method.getSimpleName().toString();
5177 if (serialMethodNames.contains(name)) {
5178 switch (name) {
5179 case "writeObject" -> checkWriteObject(tree, e, method);
5180 case "writeReplace" -> {hasWriteReplace[0] = true; hasAppropriateWriteReplace(tree, method, true);}
5181 case "readObject" -> checkReadObject(tree,e, method);
5182 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5183 case "readResolve" -> checkReadResolve(tree, e, method);
5184 default -> throw new AssertionError();
5185 }
5186 }
5187 }
5188 }
5189 });
5190 }
5191 if (!hasWriteReplace[0] &&
5192 (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
5193 !c.isAbstract() && !c.isRecord() &&
5194 types.unboxedType(c.type) == Type.noType) {
5195 // we need to check if the class is inheriting an appropriate writeReplace method
5196 MethodSymbol ms = null;
5197 Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log);
5198 try {
5199 ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
5200 } catch (FatalError fe) {
5201 // ignore no method was found
5202 } finally {
5203 log.popDiagnosticHandler(discardHandler);
5204 }
5205 if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
5206 log.warning(p.pos(),
5207 c.isValueClass() ? LintWarnings.SerializableValueClassWithoutWriteReplace1 :
5208 LintWarnings.SerializableValueClassWithoutWriteReplace2);
5209 }
5210 }
5211 return null;
5212 }
5213
5214 boolean canBeSerialized(Type type) {
5215 return type.isPrimitive() || rs.isSerializable(type);
5216 }
5217
5218 private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
5219 while (c.getKind() == ElementKind.CLASS) {
5220 Type sup = ((ClassSymbol)c).getSuperclass();
5221 if (!sup.hasTag(CLASS) || sup.isErroneous() ||
5222 sup.tsym == syms.objectType.tsym) {
5223 return false;
5224 }
5225 // if it is a value super class it has to be abstract
5226 if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
5227 return true;
5228 }
5229 c = sup.tsym;
5230 }
5231 return false;
5232 }
5233
5234 /**
5235 * Check that Externalizable class needs a public no-arg
5236 * constructor.
5237 *
5238 * Check that a Serializable class has access to the no-arg
5239 * constructor of its first nonserializable superclass.
5240 */
5241 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5242 if (isExternalizable(c.type)) {
5243 for(var sym : c.getEnclosedElements()) {
5244 if (sym.isConstructor() &&
5245 ((sym.flags() & PUBLIC) == PUBLIC)) {
5246 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5247 return;
5248 }
5249 }
5250 }
5251 log.warning(tree.pos(),
5252 LintWarnings.ExternalizableMissingPublicNoArgCtor);
5253 } else {
5337 // Warn if serialPersistentFields is initialized to a
5338 // literal null.
5339 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5340 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5341 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5342 JCExpression initExpr = variableDef.init;
5343 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5344 log.warning(initExpr.pos(),
5345 LintWarnings.SPFNullInit);
5346 }
5347 }
5348 }
5349
5350 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5351 // The "synchronized" modifier is seen in the wild on
5352 // readObject and writeObject methods and is generally
5353 // innocuous.
5354
5355 // private void writeObject(ObjectOutputStream stream) throws IOException
5356 checkPrivateNonStaticMethod(tree, method);
5357 isExpectedReturnType(tree, method, syms.voidType, true);
5358 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5359 hasExpectedExceptions(tree, method, true, syms.ioExceptionType);
5360 checkExternalizable(tree, e, method);
5361 }
5362
5363 private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
5364 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5365 // ObjectStreamException
5366
5367 // Excluding abstract, could have a more complicated
5368 // rule based on abstract-ness of the class
5369 return isConcreteInstanceMethod(tree, method, warn) &&
5370 isExpectedReturnType(tree, method, syms.objectType, warn) &&
5371 hasNoArgs(tree, method, warn) &&
5372 hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
5373 }
5374
5375 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5376 // The "synchronized" modifier is seen in the wild on
5377 // readObject and writeObject methods and is generally
5378 // innocuous.
5379
5380 // private void readObject(ObjectInputStream stream)
5381 // throws IOException, ClassNotFoundException
5382 checkPrivateNonStaticMethod(tree, method);
5383 isExpectedReturnType(tree, method, syms.voidType, true);
5384 checkOneArg(tree, e, method, syms.objectInputStreamType);
5385 hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType);
5386 checkExternalizable(tree, e, method);
5387 }
5388
5389 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5390 // private void readObjectNoData() throws ObjectStreamException
5391 checkPrivateNonStaticMethod(tree, method);
5392 isExpectedReturnType(tree, method, syms.voidType, true);
5393 hasNoArgs(tree, method, true);
5394 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5395 checkExternalizable(tree, e, method);
5396 }
5397
5398 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5399 // ANY-ACCESS-MODIFIER Object readResolve()
5400 // throws ObjectStreamException
5401
5402 // Excluding abstract, could have a more complicated
5403 // rule based on abstract-ness of the class
5404 isConcreteInstanceMethod(tree, method, true);
5405 isExpectedReturnType(tree, method, syms.objectType, true);
5406 hasNoArgs(tree, method, true);
5407 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5408 }
5409
5410 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5411 //public void writeExternal(ObjectOutput) throws IOException
5412 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5413 }
5414
5415 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5416 // public void readExternal(ObjectInput) throws IOException
5417 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5418 }
5419
5420 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5421 boolean isExtern) {
5422 if (isExtern && isExternMethod(tree, e, method, argType)) {
5423 log.warning(
5424 TreeInfo.diagnosticPositionFor(method, tree),
5425 LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5426 }
5427 }
5637 case FIELD -> {
5638 var field = (VarSymbol)enclosed;
5639 switch(name) {
5640 case "serialPersistentFields" -> {
5641 log.warning(
5642 TreeInfo.diagnosticPositionFor(field, tree),
5643 LintWarnings.IneffectualSerialFieldRecord);
5644 }
5645
5646 case "serialVersionUID" -> {
5647 // Could generate additional warning that
5648 // svuid value is not checked to match for
5649 // records.
5650 checkSerialVersionUID(tree, e, field);
5651 }}
5652 }
5653
5654 case METHOD -> {
5655 var method = (MethodSymbol)enclosed;
5656 switch(name) {
5657 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5658 case "readResolve" -> checkReadResolve(tree, e, method);
5659
5660 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5661 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5662
5663 default -> {
5664 if (serialMethodNames.contains(name)) {
5665 log.warning(
5666 TreeInfo.diagnosticPositionFor(method, tree),
5667 LintWarnings.IneffectualSerialMethodRecord(name));
5668 }
5669 }}
5670 }}});
5671 }
5672 return null;
5673 }
5674
5675 boolean isConcreteInstanceMethod(JCClassDecl tree,
5676 MethodSymbol method,
5677 boolean warn) {
5678 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5679 if (warn) {
5680 log.warning(
5681 TreeInfo.diagnosticPositionFor(method, tree),
5682 LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5683 }
5684 return false;
5685 }
5686 return true;
5687 }
5688
5689 private boolean isExpectedReturnType(JCClassDecl tree,
5690 MethodSymbol method,
5691 Type expectedReturnType,
5692 boolean warn) {
5693 // Note: there may be complications checking writeReplace
5694 // and readResolve since they return Object and could, in
5695 // principle, have covariant overrides and any synthetic
5696 // bridge method would not be represented here for
5697 // checking.
5698 Type rtype = method.getReturnType();
5699 if (!types.isSameType(expectedReturnType, rtype)) {
5700 if (warn) {
5701 log.warning(
5702 TreeInfo.diagnosticPositionFor(method, tree),
5703 LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5704 rtype, expectedReturnType));
5705 }
5706 return false;
5707 }
5708 return true;
5709 }
5710
5711 private void checkOneArg(JCClassDecl tree,
5712 Element enclosing,
5713 MethodSymbol method,
5714 Type expectedType) {
5715 String name = method.getSimpleName().toString();
5716
5717 var parameters= method.getParameters();
5718
5719 if (parameters.size() != 1) {
5720 log.warning(
5721 TreeInfo.diagnosticPositionFor(method, tree),
5722 LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5723 return;
5724 }
5725
5726 Type parameterType = parameters.get(0).asType();
5727 if (!types.isSameType(parameterType, expectedType)) {
5728 log.warning(
5729 TreeInfo.diagnosticPositionFor(method, tree),
5730 LintWarnings.SerialMethodParameterType(method.getSimpleName(),
5731 expectedType,
5732 parameterType));
5733 }
5734 }
5735
5736 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5737 Element enclosing,
5738 MethodSymbol method,
5739 Type expectedType) {
5740 var parameters = method.getParameters();
5741 return (parameters.size() == 1) &&
5742 types.isSameType(parameters.get(0).asType(), expectedType);
5743 }
5744
5745
5746 boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
5747 var parameters = method.getParameters();
5748 if (!parameters.isEmpty()) {
5749 if (warn) {
5750 log.warning(
5751 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5752 LintWarnings.SerialMethodNoArgs(method.getSimpleName()));
5753 }
5754 return false;
5755 }
5756 return true;
5757 }
5758
5759 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5760 // If the enclosing class is externalizable, warn for the method
5761 if (isExternalizable((Type)enclosing.asType())) {
5762 log.warning(
5763 TreeInfo.diagnosticPositionFor(method, tree),
5764 LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5765 }
5766 return;
5767 }
5768
5769 private boolean hasExpectedExceptions(JCClassDecl tree,
5770 MethodSymbol method,
5771 boolean warn,
5772 Type... declaredExceptions) {
5773 for (Type thrownType: method.getThrownTypes()) {
5774 // For each exception in the throws clause of the
5775 // method, if not an Error and not a RuntimeException,
5776 // check if the exception is a subtype of a declared
5777 // exception from the throws clause of the
5778 // serialization method in question.
5779 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5780 types.isSubtype(thrownType, syms.errorType) ) {
5781 continue;
5782 } else {
5783 boolean declared = false;
5784 for (Type declaredException : declaredExceptions) {
5785 if (types.isSubtype(thrownType, declaredException)) {
5786 declared = true;
5787 continue;
5788 }
5789 }
5790 if (!declared) {
5791 if (warn) {
5792 log.warning(
5793 TreeInfo.diagnosticPositionFor(method, tree),
5794 LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(),
5795 thrownType));
5796 }
5797 return false;
5798 }
5799 }
5800 }
5801 return true;
5802 }
5803
5804 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5805 Lint prevLint = lint;
5806 try {
5807 lint = lint.augment((Symbol) symbol);
5808
5809 if (lint.isEnabled(LintCategory.SERIAL)) {
5810 task.accept(symbol, p);
5811 }
5812
5813 return null;
5814 } finally {
5815 lint = prevLint;
5816 }
5817 }
5818
5819 }
5820
5821 }
|