150 Options options = Options.instance(context);
151 lint = Lint.instance(context);
152 fileManager = context.get(JavaFileManager.class);
153
154 source = Source.instance(context);
155 target = Target.instance(context);
156 warnOnAnyAccessToMembers = options.isSet("warnOnAccessToMembers");
157
158 disablePreviewCheck = false;
159
160 Target target = Target.instance(context);
161 syntheticNameChar = target.syntheticNameChar();
162
163 profile = Profile.instance(context);
164 preview = Preview.instance(context);
165
166 allowModules = Feature.MODULES.allowedInSource(source);
167 allowRecords = Feature.RECORDS.allowedInSource(source);
168 allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
169 allowPrimitivePatterns = preview.isEnabled() && Feature.PRIMITIVE_PATTERNS.allowedInSource(source);
170 }
171
172 /** Character for synthetic names
173 */
174 char syntheticNameChar;
175
176 /** A table mapping flat names of all compiled classes for each module in this run
177 * to their symbols; maintained from outside.
178 */
179 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
180
181 /** Are modules allowed
182 */
183 private final boolean allowModules;
184
185 /** Are records allowed
186 */
187 private final boolean allowRecords;
188
189 /** Are sealed classes allowed
190 */
191 private final boolean allowSealed;
192
193 /** Are primitive patterns allowed
194 */
195 private final boolean allowPrimitivePatterns;
196
197 /** Whether to force suppression of deprecation and preview warnings.
198 * This happens when attributing import statements for JDK 9+.
199 * @see Feature#DEPRECATION_ON_IMPORT
200 */
201 private boolean importSuppression;
202
203 /* *************************************************************************
204 * Errors and Warnings
205 **************************************************************************/
206
207 Lint setLint(Lint newLint) {
208 Lint prev = lint;
209 lint = newLint;
210 return prev;
211 }
212
213 boolean setImportSuppression(boolean newImportSuppression) {
214 boolean prev = importSuppression;
215 importSuppression = newImportSuppression;
216 return prev;
711 args = args.tail;
712 }
713 }
714 return t;
715 }
716
717 /** Check that type is a reference type, i.e. a class, interface or array type
718 * or a type variable.
719 * @param pos Position to be used for error reporting.
720 * @param t The type to be checked.
721 */
722 Type checkRefType(DiagnosticPosition pos, Type t) {
723 if (t.isReference())
724 return t;
725 else
726 return typeTagError(pos,
727 diags.fragment(Fragments.TypeReqRef),
728 t);
729 }
730
731 /** Check that each type is a reference type, i.e. a class, interface or array type
732 * or a type variable.
733 * @param trees Original trees, used for error reporting.
734 * @param types The types to be checked.
735 */
736 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
737 List<JCExpression> tl = trees;
738 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
739 l.head = checkRefType(tl.head.pos(), l.head);
740 tl = tl.tail;
741 }
742 return types;
743 }
744
745 /** Check that type is a null or reference type.
746 * @param pos Position to be used for error reporting.
747 * @param t The type to be checked.
748 */
749 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
750 if (t.isReference() || t.hasTag(BOT))
1102 * Warning: we can't use flags() here since this method
1103 * is called during class enter, when flags() would cause a premature
1104 * completion.
1105 * @param flags The set of modifiers given in a definition.
1106 * @param sym The defined symbol.
1107 * @param tree The declaration
1108 */
1109 long checkFlags(long flags, Symbol sym, JCTree tree) {
1110 final DiagnosticPosition pos = tree.pos();
1111 long mask;
1112 long implicit = 0;
1113
1114 switch (sym.kind) {
1115 case VAR:
1116 if (TreeInfo.isReceiverParam(tree))
1117 mask = ReceiverParamFlags;
1118 else if (sym.owner.kind != TYP)
1119 mask = LocalVarFlags;
1120 else if ((sym.owner.flags_field & INTERFACE) != 0)
1121 mask = implicit = InterfaceVarFlags;
1122 else
1123 mask = VarFlags;
1124 break;
1125 case MTH:
1126 if (sym.name == names.init) {
1127 if ((sym.owner.flags_field & ENUM) != 0) {
1128 // enum constructors cannot be declared public or
1129 // protected and must be implicitly or explicitly
1130 // private
1131 implicit = PRIVATE;
1132 mask = PRIVATE;
1133 } else
1134 mask = ConstructorFlags;
1135 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1136 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1137 mask = AnnotationTypeElementMask;
1138 implicit = PUBLIC | ABSTRACT;
1139 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1140 mask = InterfaceMethodMask;
1141 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1142 if ((flags & DEFAULT) != 0) {
1143 implicit |= ABSTRACT;
1144 }
1145 } else {
1146 mask = implicit = InterfaceMethodFlags;
1147 }
1148 } else if ((sym.owner.flags_field & RECORD) != 0) {
1149 mask = RecordMethodFlags;
1150 } else {
1151 mask = MethodFlags;
1152 }
1153 if ((flags & STRICTFP) != 0) {
1154 log.warning(tree.pos(), LintWarnings.Strictfp);
1155 }
1156 // Imply STRICTFP if owner has STRICTFP set.
1157 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1158 ((flags) & Flags.DEFAULT) != 0)
1159 implicit |= sym.owner.flags_field & STRICTFP;
1160 break;
1161 case TYP:
1162 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1163 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1164 boolean implicitlyStatic = !sym.isAnonymous() &&
1165 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1166 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1167 // local statics are allowed only if records are allowed too
1168 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
1169 implicit = implicitlyStatic ? STATIC : implicit;
1170 } else if (sym.owner.kind == TYP) {
1171 // statics in inner classes are allowed only if records are allowed too
1172 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1173 if (sym.owner.owner.kind == PCK ||
1174 (sym.owner.flags_field & STATIC) != 0) {
1175 mask |= STATIC;
1176 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1177 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1178 }
1179 // Nested interfaces and enums are always STATIC (Spec ???)
1180 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1181 } else {
1182 mask = ExtendedClassFlags;
1183 }
1184 // Interfaces are always ABSTRACT
1185 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1186
1187 if ((flags & ENUM) != 0) {
1188 // enums can't be declared abstract, final, sealed or non-sealed
1189 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
1190 implicit |= implicitEnumFinalFlag(tree);
1191 }
1192 if ((flags & RECORD) != 0) {
1193 // records can't be declared abstract
1194 mask &= ~ABSTRACT;
1195 implicit |= FINAL;
1196 }
1197 if ((flags & STRICTFP) != 0) {
1198 log.warning(tree.pos(), LintWarnings.Strictfp);
1199 }
1200 // Imply STRICTFP if owner has STRICTFP set.
1201 implicit |= sym.owner.flags_field & STRICTFP;
1202 break;
1203 default:
1204 throw new AssertionError();
1205 }
1206 long illegal = flags & ExtendedStandardFlags & ~mask;
1207 if (illegal != 0) {
1208 if ((illegal & INTERFACE) != 0) {
1209 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1210 mask |= INTERFACE;
1211 }
1212 else {
1213 log.error(pos,
1214 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1215 }
1216 }
1217 else if ((sym.kind == TYP ||
1218 // ISSUE: Disallowing abstract&private is no longer appropriate
1219 // in the presence of inner classes. Should it be deleted here?
1220 checkDisjoint(pos, flags,
1221 ABSTRACT,
1222 PRIVATE | STATIC | DEFAULT))
1223 &&
1224 checkDisjoint(pos, flags,
1225 STATIC | PRIVATE,
1226 DEFAULT)
1227 &&
1228 checkDisjoint(pos, flags,
1229 ABSTRACT | INTERFACE,
1230 FINAL | NATIVE | SYNCHRONIZED)
1231 &&
1232 checkDisjoint(pos, flags,
1233 PUBLIC,
1234 PRIVATE | PROTECTED)
1235 &&
1236 checkDisjoint(pos, flags,
1237 PRIVATE,
1238 PUBLIC | PROTECTED)
1239 &&
1240 checkDisjoint(pos, flags,
1241 FINAL,
1242 VOLATILE)
1243 &&
1244 (sym.kind == TYP ||
1245 checkDisjoint(pos, flags,
1246 ABSTRACT | NATIVE,
1247 STRICTFP))
1248 && checkDisjoint(pos, flags,
1249 FINAL,
1250 SEALED | NON_SEALED)
1251 && checkDisjoint(pos, flags,
1252 SEALED,
1253 FINAL | NON_SEALED)
1254 && checkDisjoint(pos, flags,
1255 SEALED,
1256 ANNOTATION)) {
1257 // skip
1258 }
1259 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1260 }
1261
1262 /** Determine if this enum should be implicitly final.
1263 *
1264 * If the enum has no specialized enum constants, it is final.
1265 *
1266 * If the enum does have specialized enum constants, it is
1267 * <i>not</i> final.
1268 */
1269 private long implicitEnumFinalFlag(JCTree tree) {
1270 if (!tree.hasTag(CLASSDEF)) return 0;
1271 class SpecialTreeVisitor extends JCTree.Visitor {
1272 boolean specialized;
1273 SpecialTreeVisitor() {
1274 this.specialized = false;
1275 }
1276
2033 return true;
2034 }
2035 }
2036 }
2037 return false;
2038 }
2039
2040 /** Check that a given method conforms with any method it overrides.
2041 * @param tree The tree from which positions are extracted
2042 * for errors.
2043 * @param m The overriding method.
2044 */
2045 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2046 ClassSymbol origin = (ClassSymbol)m.owner;
2047 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2048 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2049 log.error(tree.pos(), Errors.EnumNoFinalize);
2050 return;
2051 }
2052 }
2053 if (allowRecords && origin.isRecord()) {
2054 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2055 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2056 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2057 if (recordComponent.isPresent()) {
2058 return;
2059 }
2060 }
2061
2062 for (Type t = origin.type; t.hasTag(CLASS);
2063 t = types.supertype(t)) {
2064 if (t != origin.type) {
2065 checkOverride(tree, t, origin, m);
2066 }
2067 for (Type t2 : types.interfaces(t)) {
2068 checkOverride(tree, t2, origin, m);
2069 }
2070 }
2071
2072 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2468 /** Check that all abstract methods implemented by a class are
2469 * mutually compatible.
2470 * @param pos Position to be used for error reporting.
2471 * @param c The class whose interfaces are checked.
2472 */
2473 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2474 List<Type> supertypes = types.interfaces(c);
2475 Type supertype = types.supertype(c);
2476 if (supertype.hasTag(CLASS) &&
2477 (supertype.tsym.flags() & ABSTRACT) != 0)
2478 supertypes = supertypes.prepend(supertype);
2479 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2480 if (!l.head.getTypeArguments().isEmpty() &&
2481 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2482 return;
2483 for (List<Type> m = supertypes; m != l; m = m.tail)
2484 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2485 return;
2486 }
2487 checkCompatibleConcretes(pos, c);
2488 }
2489
2490 /** Check that all non-override equivalent methods accessible from 'site'
2491 * are mutually compatible (JLS 8.4.8/9.4.1).
2492 *
2493 * @param pos Position to be used for error reporting.
2494 * @param site The class whose methods are checked.
2495 * @param sym The method symbol to be checked.
2496 */
2497 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2498 ClashFilter cf = new ClashFilter(site);
2499 //for each method m1 that is overridden (directly or indirectly)
2500 //by method 'sym' in 'site'...
2501
2502 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2503 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2504 for (Symbol m1 : symbolsByName) {
2505 if (!sym.overrides(m1, site.tsym, types, false)) {
2506 continue;
2507 }
4841 }
4842 } else {
4843 Assert.error("Unknown pattern: " + currentPattern.getTag());
4844 }
4845 return false;
4846 }
4847
4848 /** check if a type is a subtype of Externalizable, if that is available. */
4849 boolean isExternalizable(Type t) {
4850 try {
4851 syms.externalizableType.complete();
4852 } catch (CompletionFailure e) {
4853 return false;
4854 }
4855 return types.isSubtype(t, syms.externalizableType);
4856 }
4857
4858 /**
4859 * Check structure of serialization declarations.
4860 */
4861 public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
4862 (new SerialTypeVisitor()).visit(c, tree);
4863 }
4864
4865 /**
4866 * This visitor will warn if a serialization-related field or
4867 * method is declared in a suspicious or incorrect way. In
4868 * particular, it will warn for cases where the runtime
4869 * serialization mechanism will silently ignore a mis-declared
4870 * entity.
4871 *
4872 * Distinguished serialization-related fields and methods:
4873 *
4874 * Methods:
4875 *
4876 * private void writeObject(ObjectOutputStream stream) throws IOException
4877 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4878 *
4879 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4880 * private void readObjectNoData() throws ObjectStreamException
4881 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4882 *
4883 * Fields:
4884 *
4885 * private static final long serialVersionUID
4886 * private static final ObjectStreamField[] serialPersistentFields
4887 *
4888 * Externalizable: methods defined on the interface
4889 * public void writeExternal(ObjectOutput) throws IOException
4890 * public void readExternal(ObjectInput) throws IOException
4891 */
4892 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4893 SerialTypeVisitor() {
4894 this.lint = Check.this.lint;
4895 }
4896
4897 private static final Set<String> serialMethodNames =
4898 Set.of("writeObject", "writeReplace",
4899 "readObject", "readObjectNoData",
4900 "readResolve");
4901
4902 private static final Set<String> serialFieldNames =
4903 Set.of("serialVersionUID", "serialPersistentFields");
4904
4905 // Type of serialPersistentFields
4906 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
4907
4908 Lint lint;
4909
4910 @Override
4911 public Void defaultAction(Element e, JCClassDecl p) {
4912 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
4913 }
4914
4915 @Override
4916 public Void visitType(TypeElement e, JCClassDecl p) {
4917 runUnderLint(e, p, (symbol, param) -> super.visitType(symbol, param));
4918 return null;
4919 }
4920
4921 @Override
4922 public Void visitTypeAsClass(TypeElement e,
4923 JCClassDecl p) {
4924 // Anonymous classes filtered out by caller.
4925
4926 ClassSymbol c = (ClassSymbol)e;
4927
4928 checkCtorAccess(p, c);
4929
4930 // Check for missing serialVersionUID; check *not* done
4931 // for enums or records.
4932 VarSymbol svuidSym = null;
4933 for (Symbol sym : c.members().getSymbolsByName(names.serialVersionUID)) {
4934 if (sym.kind == VAR) {
4935 svuidSym = (VarSymbol)sym;
4936 break;
4937 }
4938 }
4939
4940 if (svuidSym == null) {
4941 log.warning(p.pos(), LintWarnings.MissingSVUID(c));
4942 }
4943
4944 // Check for serialPersistentFields to gate checks for
4945 // non-serializable non-transient instance fields
4946 boolean serialPersistentFieldsPresent =
4947 c.members()
4948 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
4949 .iterator()
4950 .hasNext();
4951
4952 // Check declarations of serialization-related methods and
4953 // fields
4954 for(Symbol el : c.getEnclosedElements()) {
4955 runUnderLint(el, p, (enclosed, tree) -> {
4956 String name = null;
4957 switch(enclosed.getKind()) {
4958 case FIELD -> {
4959 if (!serialPersistentFieldsPresent) {
4960 var flags = enclosed.flags();
4961 if ( ((flags & TRANSIENT) == 0) &&
4962 ((flags & STATIC) == 0)) {
4963 Type varType = enclosed.asType();
4964 if (!canBeSerialized(varType)) {
4965 // Note per JLS arrays are
4966 // serializable even if the
4967 // component type is not.
4968 log.warning(
4969 TreeInfo.diagnosticPositionFor(enclosed, tree),
4970 LintWarnings.NonSerializableInstanceField);
4971 } else if (varType.hasTag(ARRAY)) {
4972 ArrayType arrayType = (ArrayType)varType;
4973 Type elementType = arrayType.elemtype;
5006 // Class.getDeclaredMethod. This differs from calling
5007 // Elements.getAllMembers(TypeElement) as the latter
5008 // will also pull in default methods from
5009 // superinterfaces. In other words, the runtime checks
5010 // (which long predate default methods on interfaces)
5011 // do not admit the possibility of inheriting methods
5012 // this way, a difference from general inheritance.
5013
5014 // The current implementation just checks the enclosed
5015 // elements and does not directly check the inherited
5016 // methods. If all the types are being checked this is
5017 // less of a concern; however, there are cases that
5018 // could be missed. In particular, readResolve and
5019 // writeReplace could, in principle, by inherited from
5020 // a non-serializable superclass and thus not checked
5021 // even if compiled with a serializable child class.
5022 case METHOD -> {
5023 var method = (MethodSymbol)enclosed;
5024 name = method.getSimpleName().toString();
5025 if (serialMethodNames.contains(name)) {
5026 switch (name) {
5027 case "writeObject" -> checkWriteObject(tree, e, method);
5028 case "writeReplace" -> checkWriteReplace(tree,e, method);
5029 case "readObject" -> checkReadObject(tree,e, method);
5030 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5031 case "readResolve" -> checkReadResolve(tree, e, method);
5032 default -> throw new AssertionError();
5033 }
5034 }
5035 }
5036 }
5037 });
5038 }
5039
5040 return null;
5041 }
5042
5043 boolean canBeSerialized(Type type) {
5044 return type.isPrimitive() || rs.isSerializable(type);
5045 }
5046
5047 /**
5048 * Check that Externalizable class needs a public no-arg
5049 * constructor.
5050 *
5051 * Check that a Serializable class has access to the no-arg
5052 * constructor of its first nonserializable superclass.
5053 */
5054 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5055 if (isExternalizable(c.type)) {
5056 for(var sym : c.getEnclosedElements()) {
5057 if (sym.isConstructor() &&
5058 ((sym.flags() & PUBLIC) == PUBLIC)) {
5059 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5060 return;
5061 }
5062 }
5063 }
5064 log.warning(tree.pos(),
5065 LintWarnings.ExternalizableMissingPublicNoArgCtor);
5066 } else {
5143
5144 if (isExternalizable((Type)(e.asType()))) {
5145 log.warning(
5146 TreeInfo.diagnosticPositionFor(spf, tree),
5147 LintWarnings.IneffectualSerialFieldExternalizable);
5148 }
5149
5150 // Warn if serialPersistentFields is initialized to a
5151 // literal null.
5152 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5153 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5154 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5155 JCExpression initExpr = variableDef.init;
5156 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5157 log.warning(initExpr.pos(),
5158 LintWarnings.SPFNullInit);
5159 }
5160 }
5161 }
5162
5163 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5164 // The "synchronized" modifier is seen in the wild on
5165 // readObject and writeObject methods and is generally
5166 // innocuous.
5167
5168 // private void writeObject(ObjectOutputStream stream) throws IOException
5169 checkPrivateNonStaticMethod(tree, method);
5170 checkReturnType(tree, e, method, syms.voidType);
5171 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5172 checkExceptions(tree, e, method, syms.ioExceptionType);
5173 checkExternalizable(tree, e, method);
5174 }
5175
5176 private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
5177 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5178 // ObjectStreamException
5179
5180 // Excluding abstract, could have a more complicated
5181 // rule based on abstract-ness of the class
5182 checkConcreteInstanceMethod(tree, e, method);
5183 checkReturnType(tree, e, method, syms.objectType);
5184 checkNoArgs(tree, e, method);
5185 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5186 }
5187
5188 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5189 // The "synchronized" modifier is seen in the wild on
5190 // readObject and writeObject methods and is generally
5191 // innocuous.
5192
5193 // private void readObject(ObjectInputStream stream)
5194 // throws IOException, ClassNotFoundException
5195 checkPrivateNonStaticMethod(tree, method);
5196 checkReturnType(tree, e, method, syms.voidType);
5197 checkOneArg(tree, e, method, syms.objectInputStreamType);
5198 checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
5199 checkExternalizable(tree, e, method);
5200 }
5201
5202 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5203 // private void readObjectNoData() throws ObjectStreamException
5204 checkPrivateNonStaticMethod(tree, method);
5205 checkReturnType(tree, e, method, syms.voidType);
5206 checkNoArgs(tree, e, method);
5207 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5208 checkExternalizable(tree, e, method);
5209 }
5210
5211 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5212 // ANY-ACCESS-MODIFIER Object readResolve()
5213 // throws ObjectStreamException
5214
5215 // Excluding abstract, could have a more complicated
5216 // rule based on abstract-ness of the class
5217 checkConcreteInstanceMethod(tree, e, method);
5218 checkReturnType(tree,e, method, syms.objectType);
5219 checkNoArgs(tree, e, method);
5220 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5221 }
5222
5223 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5224 //public void writeExternal(ObjectOutput) throws IOException
5225 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5226 }
5227
5228 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5229 // public void readExternal(ObjectInput) throws IOException
5230 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5231 }
5232
5233 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5234 boolean isExtern) {
5235 if (isExtern && isExternMethod(tree, e, method, argType)) {
5236 log.warning(
5237 TreeInfo.diagnosticPositionFor(method, tree),
5238 LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5239 }
5240 }
5241
5242 void checkPrivateNonStaticMethod(JCClassDecl tree, MethodSymbol method) {
5243 var flags = method.flags();
5244 if ((flags & PRIVATE) == 0) {
5245 log.warning(
5246 TreeInfo.diagnosticPositionFor(method, tree),
5247 LintWarnings.SerialMethodNotPrivate(method.getSimpleName()));
5248 }
5249
5250 if ((flags & STATIC) != 0) {
5251 log.warning(
5252 TreeInfo.diagnosticPositionFor(method, tree),
5253 LintWarnings.SerialMethodStatic(method.getSimpleName()));
5254 }
5255 }
5256
5257 /**
5258 * Per section 1.12 "Serialization of Enum Constants" of
5259 * the serialization specification, due to the special
5260 * serialization handling of enums, any writeObject,
5261 * readObject, writeReplace, and readResolve methods are
5262 * ignored as are serialPersistentFields and
5263 * serialVersionUID fields.
5264 */
5265 @Override
5266 public Void visitTypeAsEnum(TypeElement e,
5267 JCClassDecl p) {
5268 boolean isExtern = isExternalizable((Type)e.asType());
5269 for(Element el : e.getEnclosedElements()) {
5270 runUnderLint(el, p, (enclosed, tree) -> {
5271 String name = enclosed.getSimpleName().toString();
5272 switch(enclosed.getKind()) {
5273 case FIELD -> {
5274 var field = (VarSymbol)enclosed;
5450 case FIELD -> {
5451 var field = (VarSymbol)enclosed;
5452 switch(name) {
5453 case "serialPersistentFields" -> {
5454 log.warning(
5455 TreeInfo.diagnosticPositionFor(field, tree),
5456 LintWarnings.IneffectualSerialFieldRecord);
5457 }
5458
5459 case "serialVersionUID" -> {
5460 // Could generate additional warning that
5461 // svuid value is not checked to match for
5462 // records.
5463 checkSerialVersionUID(tree, e, field);
5464 }}
5465 }
5466
5467 case METHOD -> {
5468 var method = (MethodSymbol)enclosed;
5469 switch(name) {
5470 case "writeReplace" -> checkWriteReplace(tree, e, method);
5471 case "readResolve" -> checkReadResolve(tree, e, method);
5472
5473 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5474 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5475
5476 default -> {
5477 if (serialMethodNames.contains(name)) {
5478 log.warning(
5479 TreeInfo.diagnosticPositionFor(method, tree),
5480 LintWarnings.IneffectualSerialMethodRecord(name));
5481 }
5482 }}
5483 }}});
5484 }
5485 return null;
5486 }
5487
5488 void checkConcreteInstanceMethod(JCClassDecl tree,
5489 Element enclosing,
5490 MethodSymbol method) {
5491 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5492 log.warning(
5493 TreeInfo.diagnosticPositionFor(method, tree),
5494 LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5495 }
5496 }
5497
5498 private void checkReturnType(JCClassDecl tree,
5499 Element enclosing,
5500 MethodSymbol method,
5501 Type expectedReturnType) {
5502 // Note: there may be complications checking writeReplace
5503 // and readResolve since they return Object and could, in
5504 // principle, have covariant overrides and any synthetic
5505 // bridge method would not be represented here for
5506 // checking.
5507 Type rtype = method.getReturnType();
5508 if (!types.isSameType(expectedReturnType, rtype)) {
5509 log.warning(
5510 TreeInfo.diagnosticPositionFor(method, tree),
5511 LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5512 rtype, expectedReturnType));
5513 }
5514 }
5515
5516 private void checkOneArg(JCClassDecl tree,
5517 Element enclosing,
5518 MethodSymbol method,
5519 Type expectedType) {
5520 String name = method.getSimpleName().toString();
5521
5522 var parameters= method.getParameters();
5523
5524 if (parameters.size() != 1) {
5525 log.warning(
5526 TreeInfo.diagnosticPositionFor(method, tree),
5527 LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5528 return;
5529 }
5530
5531 Type parameterType = parameters.get(0).asType();
5532 if (!types.isSameType(parameterType, expectedType)) {
5533 log.warning(
5534 TreeInfo.diagnosticPositionFor(method, tree),
5535 LintWarnings.SerialMethodParameterType(method.getSimpleName(),
5536 expectedType,
5537 parameterType));
5538 }
5539 }
5540
5541 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5542 Element enclosing,
5543 MethodSymbol method,
5544 Type expectedType) {
5545 var parameters = method.getParameters();
5546 return (parameters.size() == 1) &&
5547 types.isSameType(parameters.get(0).asType(), expectedType);
5548 }
5549
5550
5551 private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5552 var parameters = method.getParameters();
5553 if (!parameters.isEmpty()) {
5554 log.warning(
5555 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5556 LintWarnings.SerialMethodNoArgs(method.getSimpleName()));
5557 }
5558 }
5559
5560 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5561 // If the enclosing class is externalizable, warn for the method
5562 if (isExternalizable((Type)enclosing.asType())) {
5563 log.warning(
5564 TreeInfo.diagnosticPositionFor(method, tree),
5565 LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5566 }
5567 return;
5568 }
5569
5570 private void checkExceptions(JCClassDecl tree,
5571 Element enclosing,
5572 MethodSymbol method,
5573 Type... declaredExceptions) {
5574 for (Type thrownType: method.getThrownTypes()) {
5575 // For each exception in the throws clause of the
5576 // method, if not an Error and not a RuntimeException,
5577 // check if the exception is a subtype of a declared
5578 // exception from the throws clause of the
5579 // serialization method in question.
5580 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5581 types.isSubtype(thrownType, syms.errorType) ) {
5582 continue;
5583 } else {
5584 boolean declared = false;
5585 for (Type declaredException : declaredExceptions) {
5586 if (types.isSubtype(thrownType, declaredException)) {
5587 declared = true;
5588 continue;
5589 }
5590 }
5591 if (!declared) {
5592 log.warning(
5593 TreeInfo.diagnosticPositionFor(method, tree),
5594 LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(),
5595 thrownType));
5596 }
5597 }
5598 }
5599 return;
5600 }
5601
5602 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5603 Lint prevLint = lint;
5604 try {
5605 lint = lint.augment((Symbol) symbol);
5606
5607 if (lint.isEnabled(LintCategory.SERIAL)) {
5608 task.accept(symbol, p);
5609 }
5610
5611 return null;
5612 } finally {
5613 lint = prevLint;
5614 }
5615 }
5616
5617 }
5618
5619 void checkRequiresIdentity(JCTree tree, Lint lint) {
|
150 Options options = Options.instance(context);
151 lint = Lint.instance(context);
152 fileManager = context.get(JavaFileManager.class);
153
154 source = Source.instance(context);
155 target = Target.instance(context);
156 warnOnAnyAccessToMembers = options.isSet("warnOnAccessToMembers");
157
158 disablePreviewCheck = false;
159
160 Target target = Target.instance(context);
161 syntheticNameChar = target.syntheticNameChar();
162
163 profile = Profile.instance(context);
164 preview = Preview.instance(context);
165
166 allowModules = Feature.MODULES.allowedInSource(source);
167 allowRecords = Feature.RECORDS.allowedInSource(source);
168 allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
169 allowPrimitivePatterns = preview.isEnabled() && Feature.PRIMITIVE_PATTERNS.allowedInSource(source);
170 allowValueClasses = preview.isEnabled() && Feature.VALUE_CLASSES.allowedInSource(source);
171 }
172
173 /** Character for synthetic names
174 */
175 char syntheticNameChar;
176
177 /** A table mapping flat names of all compiled classes for each module in this run
178 * to their symbols; maintained from outside.
179 */
180 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
181
182 /** Are modules allowed
183 */
184 private final boolean allowModules;
185
186 /** Are records allowed
187 */
188 private final boolean allowRecords;
189
190 /** Are sealed classes allowed
191 */
192 private final boolean allowSealed;
193
194 /** Are primitive patterns allowed
195 */
196 private final boolean allowPrimitivePatterns;
197
198 /** Are value classes allowed
199 */
200 private final boolean allowValueClasses;
201
202 /** Whether to force suppression of deprecation and preview warnings.
203 * This happens when attributing import statements for JDK 9+.
204 * @see Feature#DEPRECATION_ON_IMPORT
205 */
206 private boolean importSuppression;
207
208 /* *************************************************************************
209 * Errors and Warnings
210 **************************************************************************/
211
212 Lint setLint(Lint newLint) {
213 Lint prev = lint;
214 lint = newLint;
215 return prev;
216 }
217
218 boolean setImportSuppression(boolean newImportSuppression) {
219 boolean prev = importSuppression;
220 importSuppression = newImportSuppression;
221 return prev;
716 args = args.tail;
717 }
718 }
719 return t;
720 }
721
722 /** Check that type is a reference type, i.e. a class, interface or array type
723 * or a type variable.
724 * @param pos Position to be used for error reporting.
725 * @param t The type to be checked.
726 */
727 Type checkRefType(DiagnosticPosition pos, Type t) {
728 if (t.isReference())
729 return t;
730 else
731 return typeTagError(pos,
732 diags.fragment(Fragments.TypeReqRef),
733 t);
734 }
735
736 /** Check that type is an identity type, i.e. not a value type.
737 * When not discernible statically, give it the benefit of doubt
738 * and defer to runtime.
739 *
740 * @param pos Position to be used for error reporting.
741 * @param t The type to be checked.
742 */
743 boolean checkIdentityType(DiagnosticPosition pos, Type t) {
744 if (t.hasTag(TYPEVAR)) {
745 t = types.skipTypeVars(t, false);
746 }
747 if (t.isIntersection()) {
748 IntersectionClassType ict = (IntersectionClassType)t;
749 boolean result = true;
750 for (Type component : ict.getExplicitComponents()) {
751 result &= checkIdentityType(pos, component);
752 }
753 return result;
754 }
755 if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
756 typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
757 return false;
758 }
759 return true;
760 }
761
762 /** Check that each type is a reference type, i.e. a class, interface or array type
763 * or a type variable.
764 * @param trees Original trees, used for error reporting.
765 * @param types The types to be checked.
766 */
767 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
768 List<JCExpression> tl = trees;
769 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
770 l.head = checkRefType(tl.head.pos(), l.head);
771 tl = tl.tail;
772 }
773 return types;
774 }
775
776 /** Check that type is a null or reference type.
777 * @param pos Position to be used for error reporting.
778 * @param t The type to be checked.
779 */
780 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
781 if (t.isReference() || t.hasTag(BOT))
1133 * Warning: we can't use flags() here since this method
1134 * is called during class enter, when flags() would cause a premature
1135 * completion.
1136 * @param flags The set of modifiers given in a definition.
1137 * @param sym The defined symbol.
1138 * @param tree The declaration
1139 */
1140 long checkFlags(long flags, Symbol sym, JCTree tree) {
1141 final DiagnosticPosition pos = tree.pos();
1142 long mask;
1143 long implicit = 0;
1144
1145 switch (sym.kind) {
1146 case VAR:
1147 if (TreeInfo.isReceiverParam(tree))
1148 mask = ReceiverParamFlags;
1149 else if (sym.owner.kind != TYP)
1150 mask = LocalVarFlags;
1151 else if ((sym.owner.flags_field & INTERFACE) != 0)
1152 mask = implicit = InterfaceVarFlags;
1153 else {
1154 boolean isInstanceField = (flags & STATIC) == 0;
1155 boolean isInstanceFieldOfValueClass = isInstanceField && sym.owner.type.isValueClass();
1156 boolean isRecordField = isInstanceField && (sym.owner.flags_field & RECORD) != 0;
1157 if (allowValueClasses && (isInstanceFieldOfValueClass || isRecordField)) {
1158 implicit |= FINAL | STRICT;
1159 preview.markUsesPreview(pos); // STRICT_INIT is a preview VM feature
1160 mask = ValueFieldFlags;
1161 } else {
1162 mask = VarFlags;
1163 }
1164 }
1165 break;
1166 case MTH:
1167 if (sym.name == names.init) {
1168 if ((sym.owner.flags_field & ENUM) != 0) {
1169 // enum constructors cannot be declared public or
1170 // protected and must be implicitly or explicitly
1171 // private
1172 implicit = PRIVATE;
1173 mask = PRIVATE;
1174 } else
1175 mask = ConstructorFlags;
1176 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1177 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1178 mask = AnnotationTypeElementMask;
1179 implicit = PUBLIC | ABSTRACT;
1180 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1181 mask = InterfaceMethodMask;
1182 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1183 if ((flags & DEFAULT) != 0) {
1184 implicit |= ABSTRACT;
1185 }
1186 } else {
1187 mask = implicit = InterfaceMethodFlags;
1188 }
1189 } else if ((sym.owner.flags_field & RECORD) != 0) {
1190 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1191 RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
1192 } else {
1193 // value objects do not have an associated monitor/lock
1194 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1195 MethodFlags & ~SYNCHRONIZED : MethodFlags;
1196 }
1197 if ((flags & STRICTFP) != 0) {
1198 log.warning(tree.pos(), LintWarnings.Strictfp);
1199 }
1200 // Imply STRICTFP if owner has STRICTFP set.
1201 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1202 ((flags) & Flags.DEFAULT) != 0)
1203 implicit |= sym.owner.flags_field & STRICTFP;
1204 break;
1205 case TYP:
1206 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1207 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1208 boolean implicitlyStatic = !sym.isAnonymous() &&
1209 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1210 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1211 // local statics are allowed only if records are allowed too
1212 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
1213 implicit = implicitlyStatic ? STATIC : implicit;
1214 } else if (sym.owner.kind == TYP) {
1215 // statics in inner classes are allowed only if records are allowed too
1216 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1217 if (sym.owner.owner.kind == PCK ||
1218 (sym.owner.flags_field & STATIC) != 0) {
1219 mask |= STATIC;
1220 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1221 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1222 }
1223 // Nested interfaces and enums are always STATIC (Spec ???)
1224 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1225 } else {
1226 mask = ExtendedClassFlags;
1227 }
1228 if ((flags & (VALUE_CLASS | SEALED | ABSTRACT)) == (VALUE_CLASS | SEALED) ||
1229 (flags & (VALUE_CLASS | NON_SEALED | ABSTRACT)) == (VALUE_CLASS | NON_SEALED)) {
1230 log.error(pos, Errors.NonAbstractValueClassCantBeSealedOrNonSealed);
1231 }
1232 // Interfaces are always ABSTRACT
1233 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1234
1235 if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
1236 implicit |= IDENTITY_TYPE;
1237 }
1238
1239 if ((flags & ENUM) != 0) {
1240 // enums can't be declared abstract, final, sealed or non-sealed or value
1241 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
1242 implicit |= implicitEnumFinalFlag(tree);
1243 }
1244 if ((flags & RECORD) != 0) {
1245 // records can't be declared abstract
1246 mask &= ~ABSTRACT;
1247 implicit |= FINAL;
1248 }
1249 if ((flags & STRICTFP) != 0) {
1250 log.warning(tree.pos(), LintWarnings.Strictfp);
1251 }
1252 // Imply STRICTFP if owner has STRICTFP set.
1253 implicit |= sym.owner.flags_field & STRICTFP;
1254
1255 // concrete value classes are implicitly final
1256 if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
1257 implicit |= FINAL;
1258 }
1259 break;
1260 default:
1261 throw new AssertionError();
1262 }
1263 long illegal = flags & ExtendedStandardFlags & ~mask;
1264 if (illegal != 0) {
1265 if ((illegal & INTERFACE) != 0) {
1266 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1267 mask |= INTERFACE;
1268 }
1269 else {
1270 log.error(pos,
1271 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1272 }
1273 } else if ((sym.kind == TYP ||
1274 // ISSUE: Disallowing abstract&private is no longer appropriate
1275 // in the presence of inner classes. Should it be deleted here?
1276 checkDisjoint(pos, flags,
1277 ABSTRACT,
1278 PRIVATE | STATIC | DEFAULT))
1279 &&
1280 checkDisjoint(pos, flags,
1281 STATIC | PRIVATE,
1282 DEFAULT)
1283 &&
1284 checkDisjoint(pos, flags,
1285 ABSTRACT | INTERFACE,
1286 FINAL | NATIVE | SYNCHRONIZED)
1287 &&
1288 checkDisjoint(pos, flags,
1289 PUBLIC,
1290 PRIVATE | PROTECTED)
1291 &&
1292 checkDisjoint(pos, flags,
1293 PRIVATE,
1294 PUBLIC | PROTECTED)
1295 &&
1296 // we are using `implicit` here as instance fields of value classes are implicitly final
1297 checkDisjoint(pos, flags | implicit,
1298 FINAL,
1299 VOLATILE)
1300 &&
1301 (sym.kind == TYP ||
1302 checkDisjoint(pos, flags,
1303 ABSTRACT | NATIVE,
1304 STRICTFP))
1305 && checkDisjoint(pos, flags,
1306 FINAL,
1307 SEALED | NON_SEALED)
1308 && checkDisjoint(pos, flags,
1309 SEALED,
1310 FINAL | NON_SEALED)
1311 && checkDisjoint(pos, flags,
1312 SEALED,
1313 ANNOTATION)
1314 && checkDisjoint(pos, flags,
1315 VALUE_CLASS,
1316 ANNOTATION)
1317 && checkDisjoint(pos, flags,
1318 VALUE_CLASS,
1319 INTERFACE) ) {
1320 // skip
1321 }
1322 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1323 }
1324
1325 /** Determine if this enum should be implicitly final.
1326 *
1327 * If the enum has no specialized enum constants, it is final.
1328 *
1329 * If the enum does have specialized enum constants, it is
1330 * <i>not</i> final.
1331 */
1332 private long implicitEnumFinalFlag(JCTree tree) {
1333 if (!tree.hasTag(CLASSDEF)) return 0;
1334 class SpecialTreeVisitor extends JCTree.Visitor {
1335 boolean specialized;
1336 SpecialTreeVisitor() {
1337 this.specialized = false;
1338 }
1339
2096 return true;
2097 }
2098 }
2099 }
2100 return false;
2101 }
2102
2103 /** Check that a given method conforms with any method it overrides.
2104 * @param tree The tree from which positions are extracted
2105 * for errors.
2106 * @param m The overriding method.
2107 */
2108 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2109 ClassSymbol origin = (ClassSymbol)m.owner;
2110 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2111 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2112 log.error(tree.pos(), Errors.EnumNoFinalize);
2113 return;
2114 }
2115 }
2116 if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
2117 if (m.overrides(syms.objectFinalize, origin, types, false)) {
2118 log.warning(tree.pos(), Warnings.ValueFinalize);
2119 }
2120 }
2121 if (allowRecords && origin.isRecord()) {
2122 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2123 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2124 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2125 if (recordComponent.isPresent()) {
2126 return;
2127 }
2128 }
2129
2130 for (Type t = origin.type; t.hasTag(CLASS);
2131 t = types.supertype(t)) {
2132 if (t != origin.type) {
2133 checkOverride(tree, t, origin, m);
2134 }
2135 for (Type t2 : types.interfaces(t)) {
2136 checkOverride(tree, t2, origin, m);
2137 }
2138 }
2139
2140 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2536 /** Check that all abstract methods implemented by a class are
2537 * mutually compatible.
2538 * @param pos Position to be used for error reporting.
2539 * @param c The class whose interfaces are checked.
2540 */
2541 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2542 List<Type> supertypes = types.interfaces(c);
2543 Type supertype = types.supertype(c);
2544 if (supertype.hasTag(CLASS) &&
2545 (supertype.tsym.flags() & ABSTRACT) != 0)
2546 supertypes = supertypes.prepend(supertype);
2547 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2548 if (!l.head.getTypeArguments().isEmpty() &&
2549 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2550 return;
2551 for (List<Type> m = supertypes; m != l; m = m.tail)
2552 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2553 return;
2554 }
2555 checkCompatibleConcretes(pos, c);
2556
2557 Type identitySuper = null;
2558 Type superType = types.supertype(c);
2559 if (superType.isIdentityClass())
2560 identitySuper = superType;
2561 if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
2562 log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
2563 }
2564 }
2565
2566 /** Check that all non-override equivalent methods accessible from 'site'
2567 * are mutually compatible (JLS 8.4.8/9.4.1).
2568 *
2569 * @param pos Position to be used for error reporting.
2570 * @param site The class whose methods are checked.
2571 * @param sym The method symbol to be checked.
2572 */
2573 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2574 ClashFilter cf = new ClashFilter(site);
2575 //for each method m1 that is overridden (directly or indirectly)
2576 //by method 'sym' in 'site'...
2577
2578 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2579 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2580 for (Symbol m1 : symbolsByName) {
2581 if (!sym.overrides(m1, site.tsym, types, false)) {
2582 continue;
2583 }
4917 }
4918 } else {
4919 Assert.error("Unknown pattern: " + currentPattern.getTag());
4920 }
4921 return false;
4922 }
4923
4924 /** check if a type is a subtype of Externalizable, if that is available. */
4925 boolean isExternalizable(Type t) {
4926 try {
4927 syms.externalizableType.complete();
4928 } catch (CompletionFailure e) {
4929 return false;
4930 }
4931 return types.isSubtype(t, syms.externalizableType);
4932 }
4933
4934 /**
4935 * Check structure of serialization declarations.
4936 */
4937 public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
4938 (new SerialTypeVisitor(env)).visit(c, tree);
4939 }
4940
4941 /**
4942 * This visitor will warn if a serialization-related field or
4943 * method is declared in a suspicious or incorrect way. In
4944 * particular, it will warn for cases where the runtime
4945 * serialization mechanism will silently ignore a mis-declared
4946 * entity.
4947 *
4948 * Distinguished serialization-related fields and methods:
4949 *
4950 * Methods:
4951 *
4952 * private void writeObject(ObjectOutputStream stream) throws IOException
4953 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4954 *
4955 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4956 * private void readObjectNoData() throws ObjectStreamException
4957 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4958 *
4959 * Fields:
4960 *
4961 * private static final long serialVersionUID
4962 * private static final ObjectStreamField[] serialPersistentFields
4963 *
4964 * Externalizable: methods defined on the interface
4965 * public void writeExternal(ObjectOutput) throws IOException
4966 * public void readExternal(ObjectInput) throws IOException
4967 */
4968 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4969 Env<AttrContext> env;
4970 SerialTypeVisitor(Env<AttrContext> env) {
4971 this.lint = Check.this.lint;
4972 this.env = env;
4973 }
4974
4975 private static final Set<String> serialMethodNames =
4976 Set.of("writeObject", "writeReplace",
4977 "readObject", "readObjectNoData",
4978 "readResolve");
4979
4980 private static final Set<String> serialFieldNames =
4981 Set.of("serialVersionUID", "serialPersistentFields");
4982
4983 // Type of serialPersistentFields
4984 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
4985
4986 Lint lint;
4987
4988 @Override
4989 public Void defaultAction(Element e, JCClassDecl p) {
4990 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
4991 }
4992
4993 @Override
4994 public Void visitType(TypeElement e, JCClassDecl p) {
4995 runUnderLint(e, p, (symbol, param) -> super.visitType(symbol, param));
4996 return null;
4997 }
4998
4999 @Override
5000 public Void visitTypeAsClass(TypeElement e,
5001 JCClassDecl p) {
5002 // Anonymous classes filtered out by caller.
5003
5004 ClassSymbol c = (ClassSymbol)e;
5005
5006 checkCtorAccess(p, c);
5007
5008 /* Check for missing serialVersionUID; check *not* done
5009 * for enums or records.
5010 * Migrated value classes, need the value class and its corresponding
5011 * identity class to have the same SVUID.
5012 */
5013 VarSymbol svuidSym = null;
5014 for (Symbol sym : c.members().getSymbolsByName(names.serialVersionUID)) {
5015 if (sym.kind == VAR) {
5016 svuidSym = (VarSymbol)sym;
5017 break;
5018 }
5019 }
5020
5021 if (svuidSym == null) {
5022 log.warning(p.pos(), LintWarnings.MissingSVUID(c));
5023 }
5024
5025 // Check for serialPersistentFields to gate checks for
5026 // non-serializable non-transient instance fields
5027 boolean serialPersistentFieldsPresent =
5028 c.members()
5029 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
5030 .iterator()
5031 .hasNext();
5032
5033 // Check declarations of serialization-related methods and
5034 // fields
5035 final Map<String, Symbol> declaredSerialMethodNames = new HashMap<>();
5036 for(Symbol el : c.getEnclosedElements()) {
5037 runUnderLint(el, p, (enclosed, tree) -> {
5038 String name = null;
5039 switch(enclosed.getKind()) {
5040 case FIELD -> {
5041 if (!serialPersistentFieldsPresent) {
5042 var flags = enclosed.flags();
5043 if ( ((flags & TRANSIENT) == 0) &&
5044 ((flags & STATIC) == 0)) {
5045 Type varType = enclosed.asType();
5046 if (!canBeSerialized(varType)) {
5047 // Note per JLS arrays are
5048 // serializable even if the
5049 // component type is not.
5050 log.warning(
5051 TreeInfo.diagnosticPositionFor(enclosed, tree),
5052 LintWarnings.NonSerializableInstanceField);
5053 } else if (varType.hasTag(ARRAY)) {
5054 ArrayType arrayType = (ArrayType)varType;
5055 Type elementType = arrayType.elemtype;
5088 // Class.getDeclaredMethod. This differs from calling
5089 // Elements.getAllMembers(TypeElement) as the latter
5090 // will also pull in default methods from
5091 // superinterfaces. In other words, the runtime checks
5092 // (which long predate default methods on interfaces)
5093 // do not admit the possibility of inheriting methods
5094 // this way, a difference from general inheritance.
5095
5096 // The current implementation just checks the enclosed
5097 // elements and does not directly check the inherited
5098 // methods. If all the types are being checked this is
5099 // less of a concern; however, there are cases that
5100 // could be missed. In particular, readResolve and
5101 // writeReplace could, in principle, by inherited from
5102 // a non-serializable superclass and thus not checked
5103 // even if compiled with a serializable child class.
5104 case METHOD -> {
5105 var method = (MethodSymbol)enclosed;
5106 name = method.getSimpleName().toString();
5107 if (serialMethodNames.contains(name)) {
5108 if (switch (name) {
5109 case "writeObject" -> hasAppropriateWriteObject(tree, e, method);
5110 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5111 case "readObject" -> hasAppropriateReadObject(tree, e, method);
5112 case "readObjectNoData" -> hasAppropriateReadObjectNoData(tree, e, method);
5113 case "readResolve" -> hasAppropriateReadResolve(tree, e, method);
5114 default -> throw new AssertionError();
5115 }) {
5116 declaredSerialMethodNames.put(name, el);
5117 }
5118 }
5119 }
5120 }
5121 });
5122 }
5123 if (declaredSerialMethodNames.get("writeReplace") == null &&
5124 (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
5125 !c.isAbstract() && !c.isRecord() &&
5126 types.unboxedType(c.type) == Type.noType) {
5127 /* if we are dealing with a value class or with a class with a super class that happens to
5128 * be an abstract value class, that is not declaring a proper `writeReplace` method, then we
5129 * need to make sure then that it is inheriting an appropriate one.
5130 */
5131 MethodSymbol ms = null;
5132 Log.DiagnosticHandler discardHandler = log.new DiscardDiagnosticHandler();
5133 try {
5134 ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
5135 } catch (FatalError fe) {
5136 // ignore no method was found
5137 } finally {
5138 log.popDiagnosticHandler(discardHandler);
5139 }
5140 if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
5141 log.warning(p.pos(),
5142 c.isValueClass() ? LintWarnings.SerializableValueClassWithoutWriteReplace1 :
5143 LintWarnings.SerializableValueClassWithoutWriteReplace2);
5144 }
5145 }
5146 if (c.isValueClass()) {
5147 /* Value classes are Serializable through the use of the serialization proxy pattern.
5148 * The serialization protocol does not support a standard serialized form for value classes.
5149 * The value class delegates to a serialization proxy by supplying an alternate
5150 * record or object to be serialized instead of the value class.
5151 * When the proxy is deserialized it re-constructs the value object and returns the value object.
5152 *
5153 * In particular methods:
5154 * - writeObject
5155 * - readObject and
5156 * - readObjectNoData
5157 * are not invoked for value classes, we need to warn the user about this
5158 */
5159 for (Map.Entry<String, Symbol> entry : declaredSerialMethodNames.entrySet()) {
5160 String key = entry.getKey();
5161 if (key.equals("writeObject") || key.equals("readObject") || key.equals("readObjectNoData")) {
5162 log.warning(TreeInfo.diagnosticPositionFor(entry.getValue(), p), LintWarnings.IneffectualSerialMethodValueClass(key));
5163 }
5164 }
5165 }
5166 return null;
5167 }
5168
5169 boolean canBeSerialized(Type type) {
5170 return type.isPrimitive() || rs.isSerializable(type);
5171 }
5172
5173 private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
5174 while (c.getKind() == ElementKind.CLASS) {
5175 Type sup = ((ClassSymbol)c).getSuperclass();
5176 if (!sup.hasTag(CLASS) || sup.isErroneous() ||
5177 sup.tsym == syms.objectType.tsym) {
5178 return false;
5179 }
5180 // if it is a value super class it has to be abstract
5181 if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
5182 return true;
5183 }
5184 c = sup.tsym;
5185 }
5186 return false;
5187 }
5188
5189 /**
5190 * Check that Externalizable class needs a public no-arg
5191 * constructor.
5192 *
5193 * Check that a Serializable class has access to the no-arg
5194 * constructor of its first nonserializable superclass.
5195 */
5196 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5197 if (isExternalizable(c.type)) {
5198 for(var sym : c.getEnclosedElements()) {
5199 if (sym.isConstructor() &&
5200 ((sym.flags() & PUBLIC) == PUBLIC)) {
5201 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5202 return;
5203 }
5204 }
5205 }
5206 log.warning(tree.pos(),
5207 LintWarnings.ExternalizableMissingPublicNoArgCtor);
5208 } else {
5285
5286 if (isExternalizable((Type)(e.asType()))) {
5287 log.warning(
5288 TreeInfo.diagnosticPositionFor(spf, tree),
5289 LintWarnings.IneffectualSerialFieldExternalizable);
5290 }
5291
5292 // Warn if serialPersistentFields is initialized to a
5293 // literal null.
5294 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5295 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5296 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5297 JCExpression initExpr = variableDef.init;
5298 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5299 log.warning(initExpr.pos(),
5300 LintWarnings.SPFNullInit);
5301 }
5302 }
5303 }
5304
5305 private boolean hasAppropriateWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5306 // The "synchronized" modifier is seen in the wild on
5307 // readObject and writeObject methods and is generally
5308 // innocuous.
5309
5310 // private void writeObject(ObjectOutputStream stream) throws IOException
5311 return isPrivateNonStaticMethod(tree, method) & // no short-circuit we need to log warnings
5312 isExpectedReturnType(tree, method, syms.voidType, true) &
5313 hasExpectedArg(tree, method, syms.objectOutputStreamType) &
5314 hasExpectedExceptions(tree, method, true, syms.ioExceptionType) &
5315 checkExternalizable(tree, e, method);
5316 }
5317
5318 private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
5319 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5320 // ObjectStreamException
5321
5322 // Excluding abstract, could have a more complicated
5323 // rule based on abstract-ness of the class
5324 return isConcreteInstanceMethod(tree, method, warn) & // no short-circuit we need to log warnings
5325 isExpectedReturnType(tree, method, syms.objectType, warn) &
5326 hasNoArgs(tree, method, warn) &
5327 hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
5328 }
5329
5330 private boolean hasAppropriateReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5331 // The "synchronized" modifier is seen in the wild on
5332 // readObject and writeObject methods and is generally
5333 // innocuous.
5334
5335 // private void readObject(ObjectInputStream stream)
5336 // throws IOException, ClassNotFoundException
5337 return isPrivateNonStaticMethod(tree, method) & // no short-circuit we need to log warnings
5338 isExpectedReturnType(tree, method, syms.voidType, true) &
5339 hasExpectedArg(tree, method, syms.objectInputStreamType) &
5340 hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType) &
5341 checkExternalizable(tree, e, method);
5342 }
5343
5344 private boolean hasAppropriateReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5345 // private void readObjectNoData() throws ObjectStreamException
5346 return isPrivateNonStaticMethod(tree, method) & // no short-circuit we need to log warnings
5347 isExpectedReturnType(tree, method, syms.voidType, true) &
5348 hasNoArgs(tree, method, true) &
5349 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType) &
5350 checkExternalizable(tree, e, method);
5351 }
5352
5353 private boolean hasAppropriateReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5354 // ANY-ACCESS-MODIFIER Object readResolve()
5355 // throws ObjectStreamException
5356
5357 // Excluding abstract, could have a more complicated
5358 // rule based on abstract-ness of the class
5359 return isConcreteInstanceMethod(tree, method, true) & // no short-circuit we need to log warnings
5360 isExpectedReturnType(tree, method, syms.objectType, true) &
5361 hasNoArgs(tree, method, true) &
5362 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5363 }
5364
5365 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5366 //public void writeExternal(ObjectOutput) throws IOException
5367 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5368 }
5369
5370 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5371 // public void readExternal(ObjectInput) throws IOException
5372 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5373 }
5374
5375 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5376 boolean isExtern) {
5377 if (isExtern && isExternMethod(tree, e, method, argType)) {
5378 log.warning(
5379 TreeInfo.diagnosticPositionFor(method, tree),
5380 LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5381 }
5382 }
5383
5384 boolean isPrivateNonStaticMethod(JCClassDecl tree, MethodSymbol method) {
5385 var flags = method.flags();
5386 boolean result = true;
5387 if ((flags & PRIVATE) == 0) {
5388 log.warning(
5389 TreeInfo.diagnosticPositionFor(method, tree),
5390 LintWarnings.SerialMethodNotPrivate(method.getSimpleName()));
5391 result = false;
5392 }
5393
5394 if ((flags & STATIC) != 0) {
5395 log.warning(
5396 TreeInfo.diagnosticPositionFor(method, tree),
5397 LintWarnings.SerialMethodStatic(method.getSimpleName()));
5398 result = false;
5399 }
5400 return result;
5401 }
5402
5403 /**
5404 * Per section 1.12 "Serialization of Enum Constants" of
5405 * the serialization specification, due to the special
5406 * serialization handling of enums, any writeObject,
5407 * readObject, writeReplace, and readResolve methods are
5408 * ignored as are serialPersistentFields and
5409 * serialVersionUID fields.
5410 */
5411 @Override
5412 public Void visitTypeAsEnum(TypeElement e,
5413 JCClassDecl p) {
5414 boolean isExtern = isExternalizable((Type)e.asType());
5415 for(Element el : e.getEnclosedElements()) {
5416 runUnderLint(el, p, (enclosed, tree) -> {
5417 String name = enclosed.getSimpleName().toString();
5418 switch(enclosed.getKind()) {
5419 case FIELD -> {
5420 var field = (VarSymbol)enclosed;
5596 case FIELD -> {
5597 var field = (VarSymbol)enclosed;
5598 switch(name) {
5599 case "serialPersistentFields" -> {
5600 log.warning(
5601 TreeInfo.diagnosticPositionFor(field, tree),
5602 LintWarnings.IneffectualSerialFieldRecord);
5603 }
5604
5605 case "serialVersionUID" -> {
5606 // Could generate additional warning that
5607 // svuid value is not checked to match for
5608 // records.
5609 checkSerialVersionUID(tree, e, field);
5610 }}
5611 }
5612
5613 case METHOD -> {
5614 var method = (MethodSymbol)enclosed;
5615 switch(name) {
5616 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5617 case "readResolve" -> hasAppropriateReadResolve(tree, e, method);
5618
5619 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5620 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5621
5622 default -> {
5623 if (serialMethodNames.contains(name)) {
5624 log.warning(
5625 TreeInfo.diagnosticPositionFor(method, tree),
5626 LintWarnings.IneffectualSerialMethodRecord(name));
5627 }
5628 }}
5629 }}});
5630 }
5631 return null;
5632 }
5633
5634 boolean isConcreteInstanceMethod(JCClassDecl tree,
5635 MethodSymbol method,
5636 boolean warn) {
5637 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5638 if (warn) {
5639 log.warning(
5640 TreeInfo.diagnosticPositionFor(method, tree),
5641 LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5642 }
5643 return false;
5644 }
5645 return true;
5646 }
5647
5648 private boolean isExpectedReturnType(JCClassDecl tree,
5649 MethodSymbol method,
5650 Type expectedReturnType,
5651 boolean warn) {
5652 // Note: there may be complications checking writeReplace
5653 // and readResolve since they return Object and could, in
5654 // principle, have covariant overrides and any synthetic
5655 // bridge method would not be represented here for
5656 // checking.
5657 Type rtype = method.getReturnType();
5658 if (!types.isSameType(expectedReturnType, rtype)) {
5659 if (warn) {
5660 log.warning(
5661 TreeInfo.diagnosticPositionFor(method, tree),
5662 LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5663 rtype, expectedReturnType));
5664 }
5665 return false;
5666 }
5667 return true;
5668 }
5669
5670 private boolean hasExpectedArg(JCClassDecl tree,
5671 MethodSymbol method,
5672 Type expectedType) {
5673
5674 var parameters= method.getParameters();
5675
5676 if (parameters.size() != 1) {
5677 log.warning(
5678 TreeInfo.diagnosticPositionFor(method, tree),
5679 LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5680 return false;
5681 }
5682
5683 Type parameterType = parameters.get(0).asType();
5684 if (!types.isSameType(parameterType, expectedType)) {
5685 log.warning(
5686 TreeInfo.diagnosticPositionFor(method, tree),
5687 LintWarnings.SerialMethodParameterType(method.getSimpleName(),
5688 expectedType,
5689 parameterType));
5690 return false;
5691 }
5692 return true;
5693 }
5694
5695 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5696 Element enclosing,
5697 MethodSymbol method,
5698 Type expectedType) {
5699 var parameters = method.getParameters();
5700 return (parameters.size() == 1) &&
5701 types.isSameType(parameters.get(0).asType(), expectedType);
5702 }
5703
5704
5705 boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
5706 var parameters = method.getParameters();
5707 if (!parameters.isEmpty()) {
5708 if (warn) {
5709 log.warning(
5710 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5711 LintWarnings.SerialMethodNoArgs(method.getSimpleName()));
5712 }
5713 return false;
5714 }
5715 return true;
5716 }
5717
5718 private boolean checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5719 // If the enclosing class is externalizable, warn for the method
5720 if (isExternalizable((Type)enclosing.asType())) {
5721 log.warning(
5722 TreeInfo.diagnosticPositionFor(method, tree),
5723 LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5724 return false;
5725 }
5726 return true;
5727 }
5728
5729 private boolean hasExpectedExceptions(JCClassDecl tree,
5730 MethodSymbol method,
5731 boolean warn,
5732 Type... declaredExceptions) {
5733 for (Type thrownType: method.getThrownTypes()) {
5734 // For each exception in the throws clause of the
5735 // method, if not an Error and not a RuntimeException,
5736 // check if the exception is a subtype of a declared
5737 // exception from the throws clause of the
5738 // serialization method in question.
5739 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5740 types.isSubtype(thrownType, syms.errorType) ) {
5741 continue;
5742 } else {
5743 boolean declared = false;
5744 for (Type declaredException : declaredExceptions) {
5745 if (types.isSubtype(thrownType, declaredException)) {
5746 declared = true;
5747 continue;
5748 }
5749 }
5750 if (!declared) {
5751 if (warn) {
5752 log.warning(
5753 TreeInfo.diagnosticPositionFor(method, tree),
5754 LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(),
5755 thrownType));
5756 }
5757 return false;
5758 }
5759 }
5760 }
5761 return true;
5762 }
5763
5764 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5765 Lint prevLint = lint;
5766 try {
5767 lint = lint.augment((Symbol) symbol);
5768
5769 if (lint.isEnabled(LintCategory.SERIAL)) {
5770 task.accept(symbol, p);
5771 }
5772
5773 return null;
5774 } finally {
5775 lint = prevLint;
5776 }
5777 }
5778
5779 }
5780
5781 void checkRequiresIdentity(JCTree tree, Lint lint) {
|