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;
654 /** Check that type is a class or interface type.
655 * @param pos Position to be used for error reporting.
656 * @param t The type to be checked.
657 */
658 Type checkClassType(DiagnosticPosition pos, Type t) {
659 if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
660 return typeTagError(pos,
661 diags.fragment(Fragments.TypeReqClass),
662 asTypeParam(t));
663 } else {
664 return t;
665 }
666 }
667 //where
668 private Object asTypeParam(Type t) {
669 return (t.hasTag(TYPEVAR))
670 ? diags.fragment(Fragments.TypeParameter(t))
671 : t;
672 }
673
674 /** Check that type is a valid qualifier for a constructor reference expression
675 */
676 Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
677 t = checkClassOrArrayType(pos, t);
678 if (t.hasTag(CLASS)) {
679 if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
680 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
681 t = types.createErrorType(t);
682 } else if ((t.tsym.flags() & ENUM) != 0) {
683 log.error(pos, Errors.EnumCantBeInstantiated);
684 t = types.createErrorType(t);
685 } else {
686 t = checkClassType(pos, t, true);
687 }
688 } else if (t.hasTag(ARRAY)) {
689 if (!types.isReifiable(((ArrayType)t).elemtype)) {
690 log.error(pos, Errors.GenericArrayCreation);
691 t = types.createErrorType(t);
692 }
693 }
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 }
4840 }
4841 } else {
4842 Assert.error("Unknown pattern: " + currentPattern.getTag());
4843 }
4844 return false;
4845 }
4846
4847 /** check if a type is a subtype of Externalizable, if that is available. */
4848 boolean isExternalizable(Type t) {
4849 try {
4850 syms.externalizableType.complete();
4851 } catch (CompletionFailure e) {
4852 return false;
4853 }
4854 return types.isSubtype(t, syms.externalizableType);
4855 }
4856
4857 /**
4858 * Check structure of serialization declarations.
4859 */
4860 public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
4861 (new SerialTypeVisitor()).visit(c, tree);
4862 }
4863
4864 /**
4865 * This visitor will warn if a serialization-related field or
4866 * method is declared in a suspicious or incorrect way. In
4867 * particular, it will warn for cases where the runtime
4868 * serialization mechanism will silently ignore a mis-declared
4869 * entity.
4870 *
4871 * Distinguished serialization-related fields and methods:
4872 *
4873 * Methods:
4874 *
4875 * private void writeObject(ObjectOutputStream stream) throws IOException
4876 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4877 *
4878 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4879 * private void readObjectNoData() throws ObjectStreamException
4880 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4881 *
4882 * Fields:
4883 *
4884 * private static final long serialVersionUID
4885 * private static final ObjectStreamField[] serialPersistentFields
4886 *
4887 * Externalizable: methods defined on the interface
4888 * public void writeExternal(ObjectOutput) throws IOException
4889 * public void readExternal(ObjectInput) throws IOException
4890 */
4891 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4892 SerialTypeVisitor() {
4893 this.lint = Check.this.lint;
4894 }
4895
4896 private static final Set<String> serialMethodNames =
4897 Set.of("writeObject", "writeReplace",
4898 "readObject", "readObjectNoData",
4899 "readResolve");
4900
4901 private static final Set<String> serialFieldNames =
4902 Set.of("serialVersionUID", "serialPersistentFields");
4903
4904 // Type of serialPersistentFields
4905 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
4906
4907 Lint lint;
4908
4909 @Override
4910 public Void defaultAction(Element e, JCClassDecl p) {
4911 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
4912 }
4913
4914 @Override
4915 public Void visitType(TypeElement e, JCClassDecl p) {
4916 runUnderLint(e, p, (symbol, param) -> super.visitType(symbol, param));
4917 return null;
4918 }
4919
4920 @Override
4921 public Void visitTypeAsClass(TypeElement e,
4922 JCClassDecl p) {
4923 // Anonymous classes filtered out by caller.
4924
4925 ClassSymbol c = (ClassSymbol)e;
4926
4927 checkCtorAccess(p, c);
4928
4929 // Check for missing serialVersionUID; check *not* done
4930 // for enums or records.
4931 VarSymbol svuidSym = null;
4932 for (Symbol sym : c.members().getSymbolsByName(names.serialVersionUID)) {
4933 if (sym.kind == VAR) {
4934 svuidSym = (VarSymbol)sym;
4935 break;
4936 }
4937 }
4938
4939 if (svuidSym == null) {
4940 log.warning(p.pos(), LintWarnings.MissingSVUID(c));
4941 }
4942
4943 // Check for serialPersistentFields to gate checks for
4944 // non-serializable non-transient instance fields
4945 boolean serialPersistentFieldsPresent =
4946 c.members()
4947 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
4948 .iterator()
4949 .hasNext();
4950
4951 // Check declarations of serialization-related methods and
4952 // fields
4953 for(Symbol el : c.getEnclosedElements()) {
4954 runUnderLint(el, p, (enclosed, tree) -> {
4955 String name = null;
4956 switch(enclosed.getKind()) {
4957 case FIELD -> {
4958 if (!serialPersistentFieldsPresent) {
4959 var flags = enclosed.flags();
4960 if ( ((flags & TRANSIENT) == 0) &&
4961 ((flags & STATIC) == 0)) {
4962 Type varType = enclosed.asType();
4963 if (!canBeSerialized(varType)) {
4964 // Note per JLS arrays are
4965 // serializable even if the
4966 // component type is not.
4967 log.warning(
4968 TreeInfo.diagnosticPositionFor(enclosed, tree),
4969 LintWarnings.NonSerializableInstanceField);
4970 } else if (varType.hasTag(ARRAY)) {
4971 ArrayType arrayType = (ArrayType)varType;
4972 Type elementType = arrayType.elemtype;
5005 // Class.getDeclaredMethod. This differs from calling
5006 // Elements.getAllMembers(TypeElement) as the latter
5007 // will also pull in default methods from
5008 // superinterfaces. In other words, the runtime checks
5009 // (which long predate default methods on interfaces)
5010 // do not admit the possibility of inheriting methods
5011 // this way, a difference from general inheritance.
5012
5013 // The current implementation just checks the enclosed
5014 // elements and does not directly check the inherited
5015 // methods. If all the types are being checked this is
5016 // less of a concern; however, there are cases that
5017 // could be missed. In particular, readResolve and
5018 // writeReplace could, in principle, by inherited from
5019 // a non-serializable superclass and thus not checked
5020 // even if compiled with a serializable child class.
5021 case METHOD -> {
5022 var method = (MethodSymbol)enclosed;
5023 name = method.getSimpleName().toString();
5024 if (serialMethodNames.contains(name)) {
5025 switch (name) {
5026 case "writeObject" -> checkWriteObject(tree, e, method);
5027 case "writeReplace" -> checkWriteReplace(tree,e, method);
5028 case "readObject" -> checkReadObject(tree,e, method);
5029 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5030 case "readResolve" -> checkReadResolve(tree, e, method);
5031 default -> throw new AssertionError();
5032 }
5033 }
5034 }
5035 }
5036 });
5037 }
5038
5039 return null;
5040 }
5041
5042 boolean canBeSerialized(Type type) {
5043 return type.isPrimitive() || rs.isSerializable(type);
5044 }
5045
5046 /**
5047 * Check that Externalizable class needs a public no-arg
5048 * constructor.
5049 *
5050 * Check that a Serializable class has access to the no-arg
5051 * constructor of its first nonserializable superclass.
5052 */
5053 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5054 if (isExternalizable(c.type)) {
5055 for(var sym : c.getEnclosedElements()) {
5056 if (sym.isConstructor() &&
5057 ((sym.flags() & PUBLIC) == PUBLIC)) {
5058 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5059 return;
5060 }
5061 }
5062 }
5063 log.warning(tree.pos(),
5064 LintWarnings.ExternalizableMissingPublicNoArgCtor);
5065 } else {
5142
5143 if (isExternalizable((Type)(e.asType()))) {
5144 log.warning(
5145 TreeInfo.diagnosticPositionFor(spf, tree),
5146 LintWarnings.IneffectualSerialFieldExternalizable);
5147 }
5148
5149 // Warn if serialPersistentFields is initialized to a
5150 // literal null.
5151 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5152 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5153 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5154 JCExpression initExpr = variableDef.init;
5155 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5156 log.warning(initExpr.pos(),
5157 LintWarnings.SPFNullInit);
5158 }
5159 }
5160 }
5161
5162 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5163 // The "synchronized" modifier is seen in the wild on
5164 // readObject and writeObject methods and is generally
5165 // innocuous.
5166
5167 // private void writeObject(ObjectOutputStream stream) throws IOException
5168 checkPrivateNonStaticMethod(tree, method);
5169 checkReturnType(tree, e, method, syms.voidType);
5170 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5171 checkExceptions(tree, e, method, syms.ioExceptionType);
5172 checkExternalizable(tree, e, method);
5173 }
5174
5175 private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
5176 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5177 // ObjectStreamException
5178
5179 // Excluding abstract, could have a more complicated
5180 // rule based on abstract-ness of the class
5181 checkConcreteInstanceMethod(tree, e, method);
5182 checkReturnType(tree, e, method, syms.objectType);
5183 checkNoArgs(tree, e, method);
5184 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5185 }
5186
5187 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5188 // The "synchronized" modifier is seen in the wild on
5189 // readObject and writeObject methods and is generally
5190 // innocuous.
5191
5192 // private void readObject(ObjectInputStream stream)
5193 // throws IOException, ClassNotFoundException
5194 checkPrivateNonStaticMethod(tree, method);
5195 checkReturnType(tree, e, method, syms.voidType);
5196 checkOneArg(tree, e, method, syms.objectInputStreamType);
5197 checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
5198 checkExternalizable(tree, e, method);
5199 }
5200
5201 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5202 // private void readObjectNoData() throws ObjectStreamException
5203 checkPrivateNonStaticMethod(tree, method);
5204 checkReturnType(tree, e, method, syms.voidType);
5205 checkNoArgs(tree, e, method);
5206 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5207 checkExternalizable(tree, e, method);
5208 }
5209
5210 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5211 // ANY-ACCESS-MODIFIER Object readResolve()
5212 // throws ObjectStreamException
5213
5214 // Excluding abstract, could have a more complicated
5215 // rule based on abstract-ness of the class
5216 checkConcreteInstanceMethod(tree, e, method);
5217 checkReturnType(tree,e, method, syms.objectType);
5218 checkNoArgs(tree, e, method);
5219 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5220 }
5221
5222 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5223 //public void writeExternal(ObjectOutput) throws IOException
5224 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5225 }
5226
5227 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5228 // public void readExternal(ObjectInput) throws IOException
5229 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5230 }
5231
5232 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5233 boolean isExtern) {
5234 if (isExtern && isExternMethod(tree, e, method, argType)) {
5235 log.warning(
5236 TreeInfo.diagnosticPositionFor(method, tree),
5237 LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5238 }
5239 }
5240
5241 void checkPrivateNonStaticMethod(JCClassDecl tree, MethodSymbol method) {
5242 var flags = method.flags();
5243 if ((flags & PRIVATE) == 0) {
5244 log.warning(
5245 TreeInfo.diagnosticPositionFor(method, tree),
5246 LintWarnings.SerialMethodNotPrivate(method.getSimpleName()));
5247 }
5248
5249 if ((flags & STATIC) != 0) {
5250 log.warning(
5251 TreeInfo.diagnosticPositionFor(method, tree),
5252 LintWarnings.SerialMethodStatic(method.getSimpleName()));
5253 }
5254 }
5255
5256 /**
5257 * Per section 1.12 "Serialization of Enum Constants" of
5258 * the serialization specification, due to the special
5259 * serialization handling of enums, any writeObject,
5260 * readObject, writeReplace, and readResolve methods are
5261 * ignored as are serialPersistentFields and
5262 * serialVersionUID fields.
5263 */
5264 @Override
5265 public Void visitTypeAsEnum(TypeElement e,
5266 JCClassDecl p) {
5267 boolean isExtern = isExternalizable((Type)e.asType());
5268 for(Element el : e.getEnclosedElements()) {
5269 runUnderLint(el, p, (enclosed, tree) -> {
5270 String name = enclosed.getSimpleName().toString();
5271 switch(enclosed.getKind()) {
5272 case FIELD -> {
5273 var field = (VarSymbol)enclosed;
5449 case FIELD -> {
5450 var field = (VarSymbol)enclosed;
5451 switch(name) {
5452 case "serialPersistentFields" -> {
5453 log.warning(
5454 TreeInfo.diagnosticPositionFor(field, tree),
5455 LintWarnings.IneffectualSerialFieldRecord);
5456 }
5457
5458 case "serialVersionUID" -> {
5459 // Could generate additional warning that
5460 // svuid value is not checked to match for
5461 // records.
5462 checkSerialVersionUID(tree, e, field);
5463 }}
5464 }
5465
5466 case METHOD -> {
5467 var method = (MethodSymbol)enclosed;
5468 switch(name) {
5469 case "writeReplace" -> checkWriteReplace(tree, e, method);
5470 case "readResolve" -> checkReadResolve(tree, e, method);
5471
5472 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5473 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5474
5475 default -> {
5476 if (serialMethodNames.contains(name)) {
5477 log.warning(
5478 TreeInfo.diagnosticPositionFor(method, tree),
5479 LintWarnings.IneffectualSerialMethodRecord(name));
5480 }
5481 }}
5482 }}});
5483 }
5484 return null;
5485 }
5486
5487 void checkConcreteInstanceMethod(JCClassDecl tree,
5488 Element enclosing,
5489 MethodSymbol method) {
5490 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5491 log.warning(
5492 TreeInfo.diagnosticPositionFor(method, tree),
5493 LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5494 }
5495 }
5496
5497 private void checkReturnType(JCClassDecl tree,
5498 Element enclosing,
5499 MethodSymbol method,
5500 Type expectedReturnType) {
5501 // Note: there may be complications checking writeReplace
5502 // and readResolve since they return Object and could, in
5503 // principle, have covariant overrides and any synthetic
5504 // bridge method would not be represented here for
5505 // checking.
5506 Type rtype = method.getReturnType();
5507 if (!types.isSameType(expectedReturnType, rtype)) {
5508 log.warning(
5509 TreeInfo.diagnosticPositionFor(method, tree),
5510 LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5511 rtype, expectedReturnType));
5512 }
5513 }
5514
5515 private void checkOneArg(JCClassDecl tree,
5516 Element enclosing,
5517 MethodSymbol method,
5518 Type expectedType) {
5519 String name = method.getSimpleName().toString();
5520
5521 var parameters= method.getParameters();
5522
5523 if (parameters.size() != 1) {
5524 log.warning(
5525 TreeInfo.diagnosticPositionFor(method, tree),
5526 LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5527 return;
5528 }
5529
5530 Type parameterType = parameters.get(0).asType();
5531 if (!types.isSameType(parameterType, expectedType)) {
5532 log.warning(
5533 TreeInfo.diagnosticPositionFor(method, tree),
5534 LintWarnings.SerialMethodParameterType(method.getSimpleName(),
5535 expectedType,
5536 parameterType));
5537 }
5538 }
5539
5540 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5541 Element enclosing,
5542 MethodSymbol method,
5543 Type expectedType) {
5544 var parameters = method.getParameters();
5545 return (parameters.size() == 1) &&
5546 types.isSameType(parameters.get(0).asType(), expectedType);
5547 }
5548
5549
5550 private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5551 var parameters = method.getParameters();
5552 if (!parameters.isEmpty()) {
5553 log.warning(
5554 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5555 LintWarnings.SerialMethodNoArgs(method.getSimpleName()));
5556 }
5557 }
5558
5559 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5560 // If the enclosing class is externalizable, warn for the method
5561 if (isExternalizable((Type)enclosing.asType())) {
5562 log.warning(
5563 TreeInfo.diagnosticPositionFor(method, tree),
5564 LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5565 }
5566 return;
5567 }
5568
5569 private void checkExceptions(JCClassDecl tree,
5570 Element enclosing,
5571 MethodSymbol method,
5572 Type... declaredExceptions) {
5573 for (Type thrownType: method.getThrownTypes()) {
5574 // For each exception in the throws clause of the
5575 // method, if not an Error and not a RuntimeException,
5576 // check if the exception is a subtype of a declared
5577 // exception from the throws clause of the
5578 // serialization method in question.
5579 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5580 types.isSubtype(thrownType, syms.errorType) ) {
5581 continue;
5582 } else {
5583 boolean declared = false;
5584 for (Type declaredException : declaredExceptions) {
5585 if (types.isSubtype(thrownType, declaredException)) {
5586 declared = true;
5587 continue;
5588 }
5589 }
5590 if (!declared) {
5591 log.warning(
5592 TreeInfo.diagnosticPositionFor(method, tree),
5593 LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(),
5594 thrownType));
5595 }
5596 }
5597 }
5598 return;
5599 }
5600
5601 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5602 Lint prevLint = lint;
5603 try {
5604 lint = lint.augment((Symbol) symbol);
5605
5606 if (lint.isEnabled(LintCategory.SERIAL)) {
5607 task.accept(symbol, p);
5608 }
5609
5610 return null;
5611 } finally {
5612 lint = prevLint;
5613 }
5614 }
5615
5616 }
5617
5618 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.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
171 Feature.VALUE_CLASSES.allowedInSource(source);
172 }
173
174 /** Character for synthetic names
175 */
176 char syntheticNameChar;
177
178 /** A table mapping flat names of all compiled classes for each module in this run
179 * to their symbols; maintained from outside.
180 */
181 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
182
183 /** Are modules allowed
184 */
185 private final boolean allowModules;
186
187 /** Are records allowed
188 */
189 private final boolean allowRecords;
190
191 /** Are sealed classes allowed
192 */
193 private final boolean allowSealed;
194
195 /** Are primitive patterns allowed
196 */
197 private final boolean allowPrimitivePatterns;
198
199 /** Are value classes allowed
200 */
201 private final boolean allowValueClasses;
202
203 /** Whether to force suppression of deprecation and preview warnings.
204 * This happens when attributing import statements for JDK 9+.
205 * @see Feature#DEPRECATION_ON_IMPORT
206 */
207 private boolean importSuppression;
208
209 /* *************************************************************************
210 * Errors and Warnings
211 **************************************************************************/
212
213 Lint setLint(Lint newLint) {
214 Lint prev = lint;
215 lint = newLint;
216 return prev;
217 }
218
219 boolean setImportSuppression(boolean newImportSuppression) {
220 boolean prev = importSuppression;
221 importSuppression = newImportSuppression;
222 return prev;
660 /** Check that type is a class or interface type.
661 * @param pos Position to be used for error reporting.
662 * @param t The type to be checked.
663 */
664 Type checkClassType(DiagnosticPosition pos, Type t) {
665 if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
666 return typeTagError(pos,
667 diags.fragment(Fragments.TypeReqClass),
668 asTypeParam(t));
669 } else {
670 return t;
671 }
672 }
673 //where
674 private Object asTypeParam(Type t) {
675 return (t.hasTag(TYPEVAR))
676 ? diags.fragment(Fragments.TypeParameter(t))
677 : t;
678 }
679
680 void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
681 DiagnosticPosition pos = tree.pos();
682 for (Type st : types.closure(c.type)) {
683 if (st == null || st.tsym == null || st.tsym.kind == ERR)
684 continue;
685 if (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
686 continue;
687 if (!st.tsym.isAbstract()) {
688 if (c != st.tsym) {
689 log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
690 }
691 continue;
692 }
693 // dealing with an abstract value or value super class below.
694 for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
695 if (s.kind == MTH) {
696 if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
697 log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
698 }
699 break;
700 }
701 }
702 }
703 }
704
705 /** Check that type is a valid qualifier for a constructor reference expression
706 */
707 Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
708 t = checkClassOrArrayType(pos, t);
709 if (t.hasTag(CLASS)) {
710 if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
711 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
712 t = types.createErrorType(t);
713 } else if ((t.tsym.flags() & ENUM) != 0) {
714 log.error(pos, Errors.EnumCantBeInstantiated);
715 t = types.createErrorType(t);
716 } else {
717 t = checkClassType(pos, t, true);
718 }
719 } else if (t.hasTag(ARRAY)) {
720 if (!types.isReifiable(((ArrayType)t).elemtype)) {
721 log.error(pos, Errors.GenericArrayCreation);
722 t = types.createErrorType(t);
723 }
724 }
742 args = args.tail;
743 }
744 }
745 return t;
746 }
747
748 /** Check that type is a reference type, i.e. a class, interface or array type
749 * or a type variable.
750 * @param pos Position to be used for error reporting.
751 * @param t The type to be checked.
752 */
753 Type checkRefType(DiagnosticPosition pos, Type t) {
754 if (t.isReference())
755 return t;
756 else
757 return typeTagError(pos,
758 diags.fragment(Fragments.TypeReqRef),
759 t);
760 }
761
762 /** Check that type is an identity type, i.e. not a value type.
763 * When not discernible statically, give it the benefit of doubt
764 * and defer to runtime.
765 *
766 * @param pos Position to be used for error reporting.
767 * @param t The type to be checked.
768 */
769 boolean checkIdentityType(DiagnosticPosition pos, Type t) {
770 if (t.hasTag(TYPEVAR)) {
771 t = types.skipTypeVars(t, false);
772 }
773 if (t.isIntersection()) {
774 IntersectionClassType ict = (IntersectionClassType)t;
775 boolean result = true;
776 for (Type component : ict.getExplicitComponents()) {
777 result &= checkIdentityType(pos, component);
778 }
779 return result;
780 }
781 if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
782 typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
783 return false;
784 }
785 return true;
786 }
787
788 /** Check that each type is a reference type, i.e. a class, interface or array type
789 * or a type variable.
790 * @param trees Original trees, used for error reporting.
791 * @param types The types to be checked.
792 */
793 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
794 List<JCExpression> tl = trees;
795 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
796 l.head = checkRefType(tl.head.pos(), l.head);
797 tl = tl.tail;
798 }
799 return types;
800 }
801
802 /** Check that type is a null or reference type.
803 * @param pos Position to be used for error reporting.
804 * @param t The type to be checked.
805 */
806 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
807 if (t.isReference() || t.hasTag(BOT))
1159 * Warning: we can't use flags() here since this method
1160 * is called during class enter, when flags() would cause a premature
1161 * completion.
1162 * @param flags The set of modifiers given in a definition.
1163 * @param sym The defined symbol.
1164 * @param tree The declaration
1165 */
1166 long checkFlags(long flags, Symbol sym, JCTree tree) {
1167 final DiagnosticPosition pos = tree.pos();
1168 long mask;
1169 long implicit = 0;
1170
1171 switch (sym.kind) {
1172 case VAR:
1173 if (TreeInfo.isReceiverParam(tree))
1174 mask = ReceiverParamFlags;
1175 else if (sym.owner.kind != TYP)
1176 mask = LocalVarFlags;
1177 else if ((sym.owner.flags_field & INTERFACE) != 0)
1178 mask = implicit = InterfaceVarFlags;
1179 else {
1180 boolean isInstanceField = (flags & STATIC) == 0;
1181 boolean isInstanceFieldOfValueClass = isInstanceField && sym.owner.type.isValueClass();
1182 boolean isRecordField = isInstanceField && (sym.owner.flags_field & RECORD) != 0;
1183 if (allowValueClasses && (isInstanceFieldOfValueClass || isRecordField)) {
1184 implicit |= FINAL | STRICT;
1185 mask = ValueFieldFlags;
1186 } else {
1187 mask = VarFlags;
1188 }
1189 }
1190 break;
1191 case MTH:
1192 if (sym.name == names.init) {
1193 if ((sym.owner.flags_field & ENUM) != 0) {
1194 // enum constructors cannot be declared public or
1195 // protected and must be implicitly or explicitly
1196 // private
1197 implicit = PRIVATE;
1198 mask = PRIVATE;
1199 } else
1200 mask = ConstructorFlags;
1201 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1202 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1203 mask = AnnotationTypeElementMask;
1204 implicit = PUBLIC | ABSTRACT;
1205 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1206 mask = InterfaceMethodMask;
1207 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1208 if ((flags & DEFAULT) != 0) {
1209 implicit |= ABSTRACT;
1210 }
1211 } else {
1212 mask = implicit = InterfaceMethodFlags;
1213 }
1214 } else if ((sym.owner.flags_field & RECORD) != 0) {
1215 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1216 RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
1217 } else {
1218 // value objects do not have an associated monitor/lock
1219 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1220 MethodFlags & ~SYNCHRONIZED : MethodFlags;
1221 }
1222 if ((flags & STRICTFP) != 0) {
1223 log.warning(tree.pos(), LintWarnings.Strictfp);
1224 }
1225 // Imply STRICTFP if owner has STRICTFP set.
1226 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1227 ((flags) & Flags.DEFAULT) != 0)
1228 implicit |= sym.owner.flags_field & STRICTFP;
1229 break;
1230 case TYP:
1231 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1232 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1233 boolean implicitlyStatic = !sym.isAnonymous() &&
1234 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1235 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1236 // local statics are allowed only if records are allowed too
1237 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
1238 implicit = implicitlyStatic ? STATIC : implicit;
1239 } else if (sym.owner.kind == TYP) {
1240 // statics in inner classes are allowed only if records are allowed too
1241 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1242 if (sym.owner.owner.kind == PCK ||
1243 (sym.owner.flags_field & STATIC) != 0) {
1244 mask |= STATIC;
1245 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1246 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1247 }
1248 // Nested interfaces and enums are always STATIC (Spec ???)
1249 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1250 } else {
1251 mask = ExtendedClassFlags;
1252 }
1253 if ((flags & (VALUE_CLASS | SEALED | ABSTRACT)) == (VALUE_CLASS | SEALED) ||
1254 (flags & (VALUE_CLASS | NON_SEALED | ABSTRACT)) == (VALUE_CLASS | NON_SEALED)) {
1255 log.error(pos, Errors.NonAbstractValueClassCantBeSealedOrNonSealed);
1256 }
1257 // Interfaces are always ABSTRACT
1258 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1259
1260 if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
1261 implicit |= IDENTITY_TYPE;
1262 }
1263
1264 if ((flags & ENUM) != 0) {
1265 // enums can't be declared abstract, final, sealed or non-sealed or value
1266 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
1267 implicit |= implicitEnumFinalFlag(tree);
1268 }
1269 if ((flags & RECORD) != 0) {
1270 // records can't be declared abstract
1271 mask &= ~ABSTRACT;
1272 implicit |= FINAL;
1273 }
1274 if ((flags & STRICTFP) != 0) {
1275 log.warning(tree.pos(), LintWarnings.Strictfp);
1276 }
1277 // Imply STRICTFP if owner has STRICTFP set.
1278 implicit |= sym.owner.flags_field & STRICTFP;
1279
1280 // concrete value classes are implicitly final
1281 if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
1282 implicit |= FINAL;
1283 }
1284 break;
1285 default:
1286 throw new AssertionError();
1287 }
1288 long illegal = flags & ExtendedStandardFlags & ~mask;
1289 if (illegal != 0) {
1290 if ((illegal & INTERFACE) != 0) {
1291 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1292 mask |= INTERFACE;
1293 }
1294 else {
1295 log.error(pos,
1296 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1297 }
1298 } else if ((sym.kind == TYP ||
1299 // ISSUE: Disallowing abstract&private is no longer appropriate
1300 // in the presence of inner classes. Should it be deleted here?
1301 checkDisjoint(pos, flags,
1302 ABSTRACT,
1303 PRIVATE | STATIC | DEFAULT))
1304 &&
1305 checkDisjoint(pos, flags,
1306 STATIC | PRIVATE,
1307 DEFAULT)
1308 &&
1309 checkDisjoint(pos, flags,
1310 ABSTRACT | INTERFACE,
1311 FINAL | NATIVE | SYNCHRONIZED)
1312 &&
1313 checkDisjoint(pos, flags,
1314 PUBLIC,
1315 PRIVATE | PROTECTED)
1316 &&
1317 checkDisjoint(pos, flags,
1318 PRIVATE,
1319 PUBLIC | PROTECTED)
1320 &&
1321 // we are using `implicit` here as instance fields of value classes are implicitly final
1322 checkDisjoint(pos, flags | implicit,
1323 FINAL,
1324 VOLATILE)
1325 &&
1326 (sym.kind == TYP ||
1327 checkDisjoint(pos, flags,
1328 ABSTRACT | NATIVE,
1329 STRICTFP))
1330 && checkDisjoint(pos, flags,
1331 FINAL,
1332 SEALED | NON_SEALED)
1333 && checkDisjoint(pos, flags,
1334 SEALED,
1335 FINAL | NON_SEALED)
1336 && checkDisjoint(pos, flags,
1337 SEALED,
1338 ANNOTATION)
1339 && checkDisjoint(pos, flags,
1340 VALUE_CLASS,
1341 ANNOTATION)
1342 && checkDisjoint(pos, flags,
1343 VALUE_CLASS,
1344 INTERFACE) ) {
1345 // skip
1346 }
1347 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1348 }
1349
1350 /** Determine if this enum should be implicitly final.
1351 *
1352 * If the enum has no specialized enum constants, it is final.
1353 *
1354 * If the enum does have specialized enum constants, it is
1355 * <i>not</i> final.
1356 */
1357 private long implicitEnumFinalFlag(JCTree tree) {
1358 if (!tree.hasTag(CLASSDEF)) return 0;
1359 class SpecialTreeVisitor extends JCTree.Visitor {
1360 boolean specialized;
1361 SpecialTreeVisitor() {
1362 this.specialized = false;
1363 }
1364
2121 return true;
2122 }
2123 }
2124 }
2125 return false;
2126 }
2127
2128 /** Check that a given method conforms with any method it overrides.
2129 * @param tree The tree from which positions are extracted
2130 * for errors.
2131 * @param m The overriding method.
2132 */
2133 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2134 ClassSymbol origin = (ClassSymbol)m.owner;
2135 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2136 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2137 log.error(tree.pos(), Errors.EnumNoFinalize);
2138 return;
2139 }
2140 }
2141 if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
2142 if (m.overrides(syms.objectFinalize, origin, types, false)) {
2143 log.warning(tree.pos(), Warnings.ValueFinalize);
2144 }
2145 }
2146 if (allowRecords && origin.isRecord()) {
2147 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2148 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2149 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2150 if (recordComponent.isPresent()) {
2151 return;
2152 }
2153 }
2154
2155 for (Type t = origin.type; t.hasTag(CLASS);
2156 t = types.supertype(t)) {
2157 if (t != origin.type) {
2158 checkOverride(tree, t, origin, m);
2159 }
2160 for (Type t2 : types.interfaces(t)) {
2161 checkOverride(tree, t2, origin, m);
2162 }
2163 }
2164
2165 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2561 /** Check that all abstract methods implemented by a class are
2562 * mutually compatible.
2563 * @param pos Position to be used for error reporting.
2564 * @param c The class whose interfaces are checked.
2565 */
2566 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2567 List<Type> supertypes = types.interfaces(c);
2568 Type supertype = types.supertype(c);
2569 if (supertype.hasTag(CLASS) &&
2570 (supertype.tsym.flags() & ABSTRACT) != 0)
2571 supertypes = supertypes.prepend(supertype);
2572 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2573 if (!l.head.getTypeArguments().isEmpty() &&
2574 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2575 return;
2576 for (List<Type> m = supertypes; m != l; m = m.tail)
2577 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2578 return;
2579 }
2580 checkCompatibleConcretes(pos, c);
2581
2582 Type identitySuper = null;
2583 for (Type t : types.closure(c)) {
2584 if (t != c) {
2585 if (t.isIdentityClass() && (t.tsym.flags() & VALUE_BASED) == 0)
2586 identitySuper = t;
2587 if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
2588 log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
2589 break;
2590 }
2591 }
2592 }
2593 }
2594
2595 /** Check that all non-override equivalent methods accessible from 'site'
2596 * are mutually compatible (JLS 8.4.8/9.4.1).
2597 *
2598 * @param pos Position to be used for error reporting.
2599 * @param site The class whose methods are checked.
2600 * @param sym The method symbol to be checked.
2601 */
2602 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2603 ClashFilter cf = new ClashFilter(site);
2604 //for each method m1 that is overridden (directly or indirectly)
2605 //by method 'sym' in 'site'...
2606
2607 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2608 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2609 for (Symbol m1 : symbolsByName) {
2610 if (!sym.overrides(m1, site.tsym, types, false)) {
2611 continue;
2612 }
4945 }
4946 } else {
4947 Assert.error("Unknown pattern: " + currentPattern.getTag());
4948 }
4949 return false;
4950 }
4951
4952 /** check if a type is a subtype of Externalizable, if that is available. */
4953 boolean isExternalizable(Type t) {
4954 try {
4955 syms.externalizableType.complete();
4956 } catch (CompletionFailure e) {
4957 return false;
4958 }
4959 return types.isSubtype(t, syms.externalizableType);
4960 }
4961
4962 /**
4963 * Check structure of serialization declarations.
4964 */
4965 public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
4966 (new SerialTypeVisitor(env)).visit(c, tree);
4967 }
4968
4969 /**
4970 * This visitor will warn if a serialization-related field or
4971 * method is declared in a suspicious or incorrect way. In
4972 * particular, it will warn for cases where the runtime
4973 * serialization mechanism will silently ignore a mis-declared
4974 * entity.
4975 *
4976 * Distinguished serialization-related fields and methods:
4977 *
4978 * Methods:
4979 *
4980 * private void writeObject(ObjectOutputStream stream) throws IOException
4981 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4982 *
4983 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4984 * private void readObjectNoData() throws ObjectStreamException
4985 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4986 *
4987 * Fields:
4988 *
4989 * private static final long serialVersionUID
4990 * private static final ObjectStreamField[] serialPersistentFields
4991 *
4992 * Externalizable: methods defined on the interface
4993 * public void writeExternal(ObjectOutput) throws IOException
4994 * public void readExternal(ObjectInput) throws IOException
4995 */
4996 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4997 Env<AttrContext> env;
4998 SerialTypeVisitor(Env<AttrContext> env) {
4999 this.lint = Check.this.lint;
5000 this.env = env;
5001 }
5002
5003 private static final Set<String> serialMethodNames =
5004 Set.of("writeObject", "writeReplace",
5005 "readObject", "readObjectNoData",
5006 "readResolve");
5007
5008 private static final Set<String> serialFieldNames =
5009 Set.of("serialVersionUID", "serialPersistentFields");
5010
5011 // Type of serialPersistentFields
5012 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
5013
5014 Lint lint;
5015
5016 @Override
5017 public Void defaultAction(Element e, JCClassDecl p) {
5018 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
5019 }
5020
5021 @Override
5022 public Void visitType(TypeElement e, JCClassDecl p) {
5023 runUnderLint(e, p, (symbol, param) -> super.visitType(symbol, param));
5024 return null;
5025 }
5026
5027 @Override
5028 public Void visitTypeAsClass(TypeElement e,
5029 JCClassDecl p) {
5030 // Anonymous classes filtered out by caller.
5031
5032 ClassSymbol c = (ClassSymbol)e;
5033
5034 checkCtorAccess(p, c);
5035
5036 /* Check for missing serialVersionUID; check *not* done
5037 * for enums or records.
5038 * Migrated value classes, need the value class and its corresponding
5039 * identity class to have the same SVUID.
5040 */
5041 VarSymbol svuidSym = null;
5042 for (Symbol sym : c.members().getSymbolsByName(names.serialVersionUID)) {
5043 if (sym.kind == VAR) {
5044 svuidSym = (VarSymbol)sym;
5045 break;
5046 }
5047 }
5048
5049 if (svuidSym == null) {
5050 log.warning(p.pos(), LintWarnings.MissingSVUID(c));
5051 }
5052
5053 // Check for serialPersistentFields to gate checks for
5054 // non-serializable non-transient instance fields
5055 boolean serialPersistentFieldsPresent =
5056 c.members()
5057 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
5058 .iterator()
5059 .hasNext();
5060
5061 // Check declarations of serialization-related methods and
5062 // fields
5063 final Map<String, Symbol> declaredSerialMethodNames = new HashMap<>();
5064 for(Symbol el : c.getEnclosedElements()) {
5065 runUnderLint(el, p, (enclosed, tree) -> {
5066 String name = null;
5067 switch(enclosed.getKind()) {
5068 case FIELD -> {
5069 if (!serialPersistentFieldsPresent) {
5070 var flags = enclosed.flags();
5071 if ( ((flags & TRANSIENT) == 0) &&
5072 ((flags & STATIC) == 0)) {
5073 Type varType = enclosed.asType();
5074 if (!canBeSerialized(varType)) {
5075 // Note per JLS arrays are
5076 // serializable even if the
5077 // component type is not.
5078 log.warning(
5079 TreeInfo.diagnosticPositionFor(enclosed, tree),
5080 LintWarnings.NonSerializableInstanceField);
5081 } else if (varType.hasTag(ARRAY)) {
5082 ArrayType arrayType = (ArrayType)varType;
5083 Type elementType = arrayType.elemtype;
5116 // Class.getDeclaredMethod. This differs from calling
5117 // Elements.getAllMembers(TypeElement) as the latter
5118 // will also pull in default methods from
5119 // superinterfaces. In other words, the runtime checks
5120 // (which long predate default methods on interfaces)
5121 // do not admit the possibility of inheriting methods
5122 // this way, a difference from general inheritance.
5123
5124 // The current implementation just checks the enclosed
5125 // elements and does not directly check the inherited
5126 // methods. If all the types are being checked this is
5127 // less of a concern; however, there are cases that
5128 // could be missed. In particular, readResolve and
5129 // writeReplace could, in principle, by inherited from
5130 // a non-serializable superclass and thus not checked
5131 // even if compiled with a serializable child class.
5132 case METHOD -> {
5133 var method = (MethodSymbol)enclosed;
5134 name = method.getSimpleName().toString();
5135 if (serialMethodNames.contains(name)) {
5136 if (switch (name) {
5137 case "writeObject" -> hasAppropriateWriteObject(tree, e, method);
5138 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5139 case "readObject" -> hasAppropriateReadObject(tree, e, method);
5140 case "readObjectNoData" -> hasAppropriateReadObjectNoData(tree, e, method);
5141 case "readResolve" -> hasAppropriateReadResolve(tree, e, method);
5142 default -> throw new AssertionError();
5143 }) {
5144 declaredSerialMethodNames.put(name, el);
5145 }
5146 }
5147 }
5148 }
5149 });
5150 }
5151 if (declaredSerialMethodNames.get("writeReplace") == null &&
5152 (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
5153 !c.isAbstract() && !c.isRecord() &&
5154 types.unboxedType(c.type) == Type.noType) {
5155 /* if we are dealing with a value class or with a class with a super class that happens to
5156 * be an abstract value class, that is not declaring a proper `writeReplace` method, then we
5157 * need to make sure then that it is inheriting an appropriate one.
5158 */
5159 MethodSymbol ms = null;
5160 Log.DiagnosticHandler discardHandler = log.new DiscardDiagnosticHandler();
5161 try {
5162 ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
5163 } catch (FatalError fe) {
5164 // ignore no method was found
5165 } finally {
5166 log.popDiagnosticHandler(discardHandler);
5167 }
5168 if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
5169 log.warning(p.pos(),
5170 c.isValueClass() ? LintWarnings.SerializableValueClassWithoutWriteReplace1 :
5171 LintWarnings.SerializableValueClassWithoutWriteReplace2);
5172 }
5173 }
5174 if (c.isValueClass()) {
5175 /* Value classes are Serializable through the use of the serialization proxy pattern.
5176 * The serialization protocol does not support a standard serialized form for value classes.
5177 * The value class delegates to a serialization proxy by supplying an alternate
5178 * record or object to be serialized instead of the value class.
5179 * When the proxy is deserialized it re-constructs the value object and returns the value object.
5180 *
5181 * In particular methods:
5182 * - writeObject
5183 * - readObject and
5184 * - readObjectNoData
5185 * are not invoked for value classes, we need to warn the user about this
5186 */
5187 for (Map.Entry<String, Symbol> entry : declaredSerialMethodNames.entrySet()) {
5188 String key = entry.getKey();
5189 if (key.equals("writeObject") || key.equals("readObject") || key.equals("readObjectNoData")) {
5190 log.warning(TreeInfo.diagnosticPositionFor(entry.getValue(), p), LintWarnings.IneffectualSerialMethodValueClass(key));
5191 }
5192 }
5193 }
5194 return null;
5195 }
5196
5197 boolean canBeSerialized(Type type) {
5198 return type.isPrimitive() || rs.isSerializable(type);
5199 }
5200
5201 private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
5202 while (c.getKind() == ElementKind.CLASS) {
5203 Type sup = ((ClassSymbol)c).getSuperclass();
5204 if (!sup.hasTag(CLASS) || sup.isErroneous() ||
5205 sup.tsym == syms.objectType.tsym) {
5206 return false;
5207 }
5208 // if it is a value super class it has to be abstract
5209 if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
5210 return true;
5211 }
5212 c = sup.tsym;
5213 }
5214 return false;
5215 }
5216
5217 /**
5218 * Check that Externalizable class needs a public no-arg
5219 * constructor.
5220 *
5221 * Check that a Serializable class has access to the no-arg
5222 * constructor of its first nonserializable superclass.
5223 */
5224 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5225 if (isExternalizable(c.type)) {
5226 for(var sym : c.getEnclosedElements()) {
5227 if (sym.isConstructor() &&
5228 ((sym.flags() & PUBLIC) == PUBLIC)) {
5229 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5230 return;
5231 }
5232 }
5233 }
5234 log.warning(tree.pos(),
5235 LintWarnings.ExternalizableMissingPublicNoArgCtor);
5236 } else {
5313
5314 if (isExternalizable((Type)(e.asType()))) {
5315 log.warning(
5316 TreeInfo.diagnosticPositionFor(spf, tree),
5317 LintWarnings.IneffectualSerialFieldExternalizable);
5318 }
5319
5320 // Warn if serialPersistentFields is initialized to a
5321 // literal null.
5322 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5323 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5324 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5325 JCExpression initExpr = variableDef.init;
5326 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5327 log.warning(initExpr.pos(),
5328 LintWarnings.SPFNullInit);
5329 }
5330 }
5331 }
5332
5333 private boolean hasAppropriateWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5334 // The "synchronized" modifier is seen in the wild on
5335 // readObject and writeObject methods and is generally
5336 // innocuous.
5337
5338 // private void writeObject(ObjectOutputStream stream) throws IOException
5339 return isPrivateNonStaticMethod(tree, method) & // no short-circuit we need to log warnings
5340 isExpectedReturnType(tree, method, syms.voidType, true) &
5341 hasExpectedArg(tree, method, syms.objectOutputStreamType) &
5342 hasExpectedExceptions(tree, method, true, syms.ioExceptionType) &
5343 checkExternalizable(tree, e, method);
5344 }
5345
5346 private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
5347 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5348 // ObjectStreamException
5349
5350 // Excluding abstract, could have a more complicated
5351 // rule based on abstract-ness of the class
5352 return isConcreteInstanceMethod(tree, method, warn) & // no short-circuit we need to log warnings
5353 isExpectedReturnType(tree, method, syms.objectType, warn) &
5354 hasNoArgs(tree, method, warn) &
5355 hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
5356 }
5357
5358 private boolean hasAppropriateReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5359 // The "synchronized" modifier is seen in the wild on
5360 // readObject and writeObject methods and is generally
5361 // innocuous.
5362
5363 // private void readObject(ObjectInputStream stream)
5364 // throws IOException, ClassNotFoundException
5365 return isPrivateNonStaticMethod(tree, method) & // no short-circuit we need to log warnings
5366 isExpectedReturnType(tree, method, syms.voidType, true) &
5367 hasExpectedArg(tree, method, syms.objectInputStreamType) &
5368 hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType) &
5369 checkExternalizable(tree, e, method);
5370 }
5371
5372 private boolean hasAppropriateReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5373 // private void readObjectNoData() throws ObjectStreamException
5374 return isPrivateNonStaticMethod(tree, method) & // no short-circuit we need to log warnings
5375 isExpectedReturnType(tree, method, syms.voidType, true) &
5376 hasNoArgs(tree, method, true) &
5377 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType) &
5378 checkExternalizable(tree, e, method);
5379 }
5380
5381 private boolean hasAppropriateReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5382 // ANY-ACCESS-MODIFIER Object readResolve()
5383 // throws ObjectStreamException
5384
5385 // Excluding abstract, could have a more complicated
5386 // rule based on abstract-ness of the class
5387 return isConcreteInstanceMethod(tree, method, true) & // no short-circuit we need to log warnings
5388 isExpectedReturnType(tree, method, syms.objectType, true) &
5389 hasNoArgs(tree, method, true) &
5390 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5391 }
5392
5393 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5394 //public void writeExternal(ObjectOutput) throws IOException
5395 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5396 }
5397
5398 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5399 // public void readExternal(ObjectInput) throws IOException
5400 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5401 }
5402
5403 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5404 boolean isExtern) {
5405 if (isExtern && isExternMethod(tree, e, method, argType)) {
5406 log.warning(
5407 TreeInfo.diagnosticPositionFor(method, tree),
5408 LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5409 }
5410 }
5411
5412 boolean isPrivateNonStaticMethod(JCClassDecl tree, MethodSymbol method) {
5413 var flags = method.flags();
5414 boolean result = true;
5415 if ((flags & PRIVATE) == 0) {
5416 log.warning(
5417 TreeInfo.diagnosticPositionFor(method, tree),
5418 LintWarnings.SerialMethodNotPrivate(method.getSimpleName()));
5419 result = false;
5420 }
5421
5422 if ((flags & STATIC) != 0) {
5423 log.warning(
5424 TreeInfo.diagnosticPositionFor(method, tree),
5425 LintWarnings.SerialMethodStatic(method.getSimpleName()));
5426 result = false;
5427 }
5428 return result;
5429 }
5430
5431 /**
5432 * Per section 1.12 "Serialization of Enum Constants" of
5433 * the serialization specification, due to the special
5434 * serialization handling of enums, any writeObject,
5435 * readObject, writeReplace, and readResolve methods are
5436 * ignored as are serialPersistentFields and
5437 * serialVersionUID fields.
5438 */
5439 @Override
5440 public Void visitTypeAsEnum(TypeElement e,
5441 JCClassDecl p) {
5442 boolean isExtern = isExternalizable((Type)e.asType());
5443 for(Element el : e.getEnclosedElements()) {
5444 runUnderLint(el, p, (enclosed, tree) -> {
5445 String name = enclosed.getSimpleName().toString();
5446 switch(enclosed.getKind()) {
5447 case FIELD -> {
5448 var field = (VarSymbol)enclosed;
5624 case FIELD -> {
5625 var field = (VarSymbol)enclosed;
5626 switch(name) {
5627 case "serialPersistentFields" -> {
5628 log.warning(
5629 TreeInfo.diagnosticPositionFor(field, tree),
5630 LintWarnings.IneffectualSerialFieldRecord);
5631 }
5632
5633 case "serialVersionUID" -> {
5634 // Could generate additional warning that
5635 // svuid value is not checked to match for
5636 // records.
5637 checkSerialVersionUID(tree, e, field);
5638 }}
5639 }
5640
5641 case METHOD -> {
5642 var method = (MethodSymbol)enclosed;
5643 switch(name) {
5644 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5645 case "readResolve" -> hasAppropriateReadResolve(tree, e, method);
5646
5647 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5648 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5649
5650 default -> {
5651 if (serialMethodNames.contains(name)) {
5652 log.warning(
5653 TreeInfo.diagnosticPositionFor(method, tree),
5654 LintWarnings.IneffectualSerialMethodRecord(name));
5655 }
5656 }}
5657 }}});
5658 }
5659 return null;
5660 }
5661
5662 boolean isConcreteInstanceMethod(JCClassDecl tree,
5663 MethodSymbol method,
5664 boolean warn) {
5665 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5666 if (warn) {
5667 log.warning(
5668 TreeInfo.diagnosticPositionFor(method, tree),
5669 LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5670 }
5671 return false;
5672 }
5673 return true;
5674 }
5675
5676 private boolean isExpectedReturnType(JCClassDecl tree,
5677 MethodSymbol method,
5678 Type expectedReturnType,
5679 boolean warn) {
5680 // Note: there may be complications checking writeReplace
5681 // and readResolve since they return Object and could, in
5682 // principle, have covariant overrides and any synthetic
5683 // bridge method would not be represented here for
5684 // checking.
5685 Type rtype = method.getReturnType();
5686 if (!types.isSameType(expectedReturnType, rtype)) {
5687 if (warn) {
5688 log.warning(
5689 TreeInfo.diagnosticPositionFor(method, tree),
5690 LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5691 rtype, expectedReturnType));
5692 }
5693 return false;
5694 }
5695 return true;
5696 }
5697
5698 private boolean hasExpectedArg(JCClassDecl tree,
5699 MethodSymbol method,
5700 Type expectedType) {
5701
5702 var parameters= method.getParameters();
5703
5704 if (parameters.size() != 1) {
5705 log.warning(
5706 TreeInfo.diagnosticPositionFor(method, tree),
5707 LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5708 return false;
5709 }
5710
5711 Type parameterType = parameters.get(0).asType();
5712 if (!types.isSameType(parameterType, expectedType)) {
5713 log.warning(
5714 TreeInfo.diagnosticPositionFor(method, tree),
5715 LintWarnings.SerialMethodParameterType(method.getSimpleName(),
5716 expectedType,
5717 parameterType));
5718 return false;
5719 }
5720 return true;
5721 }
5722
5723 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5724 Element enclosing,
5725 MethodSymbol method,
5726 Type expectedType) {
5727 var parameters = method.getParameters();
5728 return (parameters.size() == 1) &&
5729 types.isSameType(parameters.get(0).asType(), expectedType);
5730 }
5731
5732
5733 boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
5734 var parameters = method.getParameters();
5735 if (!parameters.isEmpty()) {
5736 if (warn) {
5737 log.warning(
5738 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5739 LintWarnings.SerialMethodNoArgs(method.getSimpleName()));
5740 }
5741 return false;
5742 }
5743 return true;
5744 }
5745
5746 private boolean checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5747 // If the enclosing class is externalizable, warn for the method
5748 if (isExternalizable((Type)enclosing.asType())) {
5749 log.warning(
5750 TreeInfo.diagnosticPositionFor(method, tree),
5751 LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5752 return false;
5753 }
5754 return true;
5755 }
5756
5757 private boolean hasExpectedExceptions(JCClassDecl tree,
5758 MethodSymbol method,
5759 boolean warn,
5760 Type... declaredExceptions) {
5761 for (Type thrownType: method.getThrownTypes()) {
5762 // For each exception in the throws clause of the
5763 // method, if not an Error and not a RuntimeException,
5764 // check if the exception is a subtype of a declared
5765 // exception from the throws clause of the
5766 // serialization method in question.
5767 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5768 types.isSubtype(thrownType, syms.errorType) ) {
5769 continue;
5770 } else {
5771 boolean declared = false;
5772 for (Type declaredException : declaredExceptions) {
5773 if (types.isSubtype(thrownType, declaredException)) {
5774 declared = true;
5775 continue;
5776 }
5777 }
5778 if (!declared) {
5779 if (warn) {
5780 log.warning(
5781 TreeInfo.diagnosticPositionFor(method, tree),
5782 LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(),
5783 thrownType));
5784 }
5785 return false;
5786 }
5787 }
5788 }
5789 return true;
5790 }
5791
5792 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5793 Lint prevLint = lint;
5794 try {
5795 lint = lint.augment((Symbol) symbol);
5796
5797 if (lint.isEnabled(LintCategory.SERIAL)) {
5798 task.accept(symbol, p);
5799 }
5800
5801 return null;
5802 } finally {
5803 lint = prevLint;
5804 }
5805 }
5806
5807 }
5808
5809 void checkRequiresIdentity(JCTree tree, Lint lint) {
|