11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.javac.comp;
27
28 import java.util.*;
29 import java.util.function.BiConsumer;
30 import java.util.function.BiPredicate;
31 import java.util.function.Consumer;
32 import java.util.function.Predicate;
33 import java.util.function.Supplier;
34 import java.util.function.ToIntBiFunction;
35 import java.util.stream.Collectors;
36 import java.util.stream.StreamSupport;
37
38 import javax.lang.model.element.ElementKind;
39 import javax.lang.model.element.NestingKind;
40 import javax.tools.JavaFileManager;
41
42 import com.sun.source.tree.CaseTree;
43 import com.sun.tools.javac.code.*;
44 import com.sun.tools.javac.code.Attribute.Compound;
45 import com.sun.tools.javac.code.Directive.ExportsDirective;
46 import com.sun.tools.javac.code.Directive.RequiresDirective;
47 import com.sun.tools.javac.code.Source.Feature;
48 import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata;
49 import com.sun.tools.javac.jvm.*;
50 import com.sun.tools.javac.resources.CompilerProperties.Errors;
51 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
62 import com.sun.tools.javac.code.Lint;
63 import com.sun.tools.javac.code.Lint.LintCategory;
64 import com.sun.tools.javac.code.Scope.WriteableScope;
65 import com.sun.tools.javac.code.Type.*;
66 import com.sun.tools.javac.code.Symbol.*;
67 import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
68 import com.sun.tools.javac.tree.JCTree.*;
69
70 import static com.sun.tools.javac.code.Flags.*;
71 import static com.sun.tools.javac.code.Flags.ANNOTATION;
72 import static com.sun.tools.javac.code.Flags.SYNCHRONIZED;
73 import static com.sun.tools.javac.code.Kinds.*;
74 import static com.sun.tools.javac.code.Kinds.Kind.*;
75 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
76 import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE;
77 import static com.sun.tools.javac.code.TypeTag.*;
78 import static com.sun.tools.javac.code.TypeTag.WILDCARD;
79
80 import static com.sun.tools.javac.tree.JCTree.Tag.*;
81 import javax.lang.model.element.Element;
82 import javax.lang.model.element.ExecutableElement;
83 import javax.lang.model.element.TypeElement;
84 import javax.lang.model.type.DeclaredType;
85 import javax.lang.model.type.TypeMirror;
86 import javax.lang.model.util.ElementFilter;
87 import javax.lang.model.util.ElementKindVisitor14;
88
89 /** Type checking helper class for the attribution phase.
90 *
91 * <p><b>This is NOT part of any supported API.
92 * If you write code that depends on this, you do so at your own risk.
93 * This code and its internal interfaces are subject to change or
94 * deletion without notice.</b>
95 */
96 public class Check {
97 protected static final Context.Key<Check> checkKey = new Context.Key<>();
98
99 // Flag bits indicating which item(s) chosen from a pair of items
100 private static final int FIRST = 0x01;
101 private static final int SECOND = 0x02;
102
103 private final Names names;
104 private final Log log;
105 private final Resolve rs;
106 private final Symtab syms;
167
168 boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION);
169 boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL);
170 boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED);
171 boolean enforceMandatoryWarnings = true;
172
173 deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated,
174 enforceMandatoryWarnings, "deprecated", LintCategory.DEPRECATION);
175 removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval,
176 enforceMandatoryWarnings, "removal", LintCategory.REMOVAL);
177 uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked,
178 enforceMandatoryWarnings, "unchecked", LintCategory.UNCHECKED);
179 sunApiHandler = new MandatoryWarningHandler(log, null, false,
180 enforceMandatoryWarnings, "sunapi", null);
181
182 deferredLintHandler = DeferredLintHandler.instance(context);
183
184 allowModules = Feature.MODULES.allowedInSource(source);
185 allowRecords = Feature.RECORDS.allowedInSource(source);
186 allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
187 }
188
189 /** Character for synthetic names
190 */
191 char syntheticNameChar;
192
193 /** A table mapping flat names of all compiled classes for each module in this run
194 * to their symbols; maintained from outside.
195 */
196 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
197
198 /** A handler for messages about deprecated usage.
199 */
200 private MandatoryWarningHandler deprecationHandler;
201
202 /** A handler for messages about deprecated-for-removal usage.
203 */
204 private MandatoryWarningHandler removalHandler;
205
206 /** A handler for messages about unchecked or unsafe usage.
210 /** A handler for messages about using proprietary API.
211 */
212 private MandatoryWarningHandler sunApiHandler;
213
214 /** A handler for deferred lint warnings.
215 */
216 private DeferredLintHandler deferredLintHandler;
217
218 /** Are modules allowed
219 */
220 private final boolean allowModules;
221
222 /** Are records allowed
223 */
224 private final boolean allowRecords;
225
226 /** Are sealed classes allowed
227 */
228 private final boolean allowSealed;
229
230 /* *************************************************************************
231 * Errors and Warnings
232 **************************************************************************/
233
234 Lint setLint(Lint newLint) {
235 Lint prev = lint;
236 lint = newLint;
237 return prev;
238 }
239
240 MethodSymbol setMethod(MethodSymbol newMethod) {
241 MethodSymbol prev = method;
242 method = newMethod;
243 return prev;
244 }
245
246 /** Warn about deprecated symbol.
247 * @param pos Position to be used for error reporting.
248 * @param sym The deprecated symbol.
249 */
743 /** Check that type is a class or interface type.
744 * @param pos Position to be used for error reporting.
745 * @param t The type to be checked.
746 */
747 Type checkClassType(DiagnosticPosition pos, Type t) {
748 if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
749 return typeTagError(pos,
750 diags.fragment(Fragments.TypeReqClass),
751 asTypeParam(t));
752 } else {
753 return t;
754 }
755 }
756 //where
757 private Object asTypeParam(Type t) {
758 return (t.hasTag(TYPEVAR))
759 ? diags.fragment(Fragments.TypeParameter(t))
760 : t;
761 }
762
763 /** Check that type is a valid qualifier for a constructor reference expression
764 */
765 Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
766 t = checkClassOrArrayType(pos, t);
767 if (t.hasTag(CLASS)) {
768 if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
769 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
770 t = types.createErrorType(t);
771 } else if ((t.tsym.flags() & ENUM) != 0) {
772 log.error(pos, Errors.EnumCantBeInstantiated);
773 t = types.createErrorType(t);
774 } else {
775 t = checkClassType(pos, t, true);
776 }
777 } else if (t.hasTag(ARRAY)) {
778 if (!types.isReifiable(((ArrayType)t).elemtype)) {
779 log.error(pos, Errors.GenericArrayCreation);
780 t = types.createErrorType(t);
781 }
782 }
800 args = args.tail;
801 }
802 }
803 return t;
804 }
805
806 /** Check that type is a reference type, i.e. a class, interface or array type
807 * or a type variable.
808 * @param pos Position to be used for error reporting.
809 * @param t The type to be checked.
810 */
811 Type checkRefType(DiagnosticPosition pos, Type t) {
812 if (t.isReference())
813 return t;
814 else
815 return typeTagError(pos,
816 diags.fragment(Fragments.TypeReqRef),
817 t);
818 }
819
820 /** Check that each type is a reference type, i.e. a class, interface or array type
821 * or a type variable.
822 * @param trees Original trees, used for error reporting.
823 * @param types The types to be checked.
824 */
825 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
826 List<JCExpression> tl = trees;
827 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
828 l.head = checkRefType(tl.head.pos(), l.head);
829 tl = tl.tail;
830 }
831 return types;
832 }
833
834 /** Check that type is a null or reference type.
835 * @param pos Position to be used for error reporting.
836 * @param t The type to be checked.
837 */
838 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
839 if (t.isReference() || t.hasTag(BOT))
1190 * return modifiers together with any implicit modifiers for that symbol.
1191 * Warning: we can't use flags() here since this method
1192 * is called during class enter, when flags() would cause a premature
1193 * completion.
1194 * @param pos Position to be used for error reporting.
1195 * @param flags The set of modifiers given in a definition.
1196 * @param sym The defined symbol.
1197 */
1198 long checkFlags(DiagnosticPosition pos, long flags, Symbol sym, JCTree tree) {
1199 long mask;
1200 long implicit = 0;
1201
1202 switch (sym.kind) {
1203 case VAR:
1204 if (TreeInfo.isReceiverParam(tree))
1205 mask = ReceiverParamFlags;
1206 else if (sym.owner.kind != TYP)
1207 mask = LocalVarFlags;
1208 else if ((sym.owner.flags_field & INTERFACE) != 0)
1209 mask = implicit = InterfaceVarFlags;
1210 else
1211 mask = VarFlags;
1212 break;
1213 case MTH:
1214 if (sym.name == names.init) {
1215 if ((sym.owner.flags_field & ENUM) != 0) {
1216 // enum constructors cannot be declared public or
1217 // protected and must be implicitly or explicitly
1218 // private
1219 implicit = PRIVATE;
1220 mask = PRIVATE;
1221 } else
1222 mask = ConstructorFlags;
1223 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1224 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1225 mask = AnnotationTypeElementMask;
1226 implicit = PUBLIC | ABSTRACT;
1227 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1228 mask = InterfaceMethodMask;
1229 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1230 if ((flags & DEFAULT) != 0) {
1231 implicit |= ABSTRACT;
1232 }
1233 } else {
1234 mask = implicit = InterfaceMethodFlags;
1235 }
1236 } else if ((sym.owner.flags_field & RECORD) != 0) {
1237 mask = RecordMethodFlags;
1238 } else {
1239 mask = MethodFlags;
1240 }
1241 if ((flags & STRICTFP) != 0) {
1242 warnOnExplicitStrictfp(pos);
1243 }
1244 // Imply STRICTFP if owner has STRICTFP set.
1245 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1246 ((flags) & Flags.DEFAULT) != 0)
1247 implicit |= sym.owner.flags_field & STRICTFP;
1248 break;
1249 case TYP:
1250 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1251 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1252 boolean implicitlyStatic = !sym.isAnonymous() &&
1253 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1254 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1255 // local statics are allowed only if records are allowed too
1256 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
1257 implicit = implicitlyStatic ? STATIC : implicit;
1258 } else if (sym.owner.kind == TYP) {
1259 // statics in inner classes are allowed only if records are allowed too
1260 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1261 if (sym.owner.owner.kind == PCK ||
1262 (sym.owner.flags_field & STATIC) != 0) {
1263 mask |= STATIC;
1264 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1265 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1266 }
1267 // Nested interfaces and enums are always STATIC (Spec ???)
1268 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1269 } else {
1270 mask = ExtendedClassFlags;
1271 }
1272 // Interfaces are always ABSTRACT
1273 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1274
1275 if ((flags & ENUM) != 0) {
1276 // enums can't be declared abstract, final, sealed or non-sealed
1277 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
1278 implicit |= implicitEnumFinalFlag(tree);
1279 }
1280 if ((flags & RECORD) != 0) {
1281 // records can't be declared abstract
1282 mask &= ~ABSTRACT;
1283 implicit |= FINAL;
1284 }
1285 if ((flags & STRICTFP) != 0) {
1286 warnOnExplicitStrictfp(pos);
1287 }
1288 // Imply STRICTFP if owner has STRICTFP set.
1289 implicit |= sym.owner.flags_field & STRICTFP;
1290 break;
1291 default:
1292 throw new AssertionError();
1293 }
1294 long illegal = flags & ExtendedStandardFlags & ~mask;
1295 if (illegal != 0) {
1296 if ((illegal & INTERFACE) != 0) {
1297 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1298 mask |= INTERFACE;
1299 }
1300 else {
1301 log.error(pos,
1302 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1303 }
1304 }
1305 else if ((sym.kind == TYP ||
1306 // ISSUE: Disallowing abstract&private is no longer appropriate
1307 // in the presence of inner classes. Should it be deleted here?
1308 checkDisjoint(pos, flags,
1309 ABSTRACT,
1310 PRIVATE | STATIC | DEFAULT))
1311 &&
1312 checkDisjoint(pos, flags,
1313 STATIC | PRIVATE,
1314 DEFAULT)
1315 &&
1316 checkDisjoint(pos, flags,
1317 ABSTRACT | INTERFACE,
1318 FINAL | NATIVE | SYNCHRONIZED)
1319 &&
1320 checkDisjoint(pos, flags,
1321 PUBLIC,
1322 PRIVATE | PROTECTED)
1323 &&
1324 checkDisjoint(pos, flags,
1325 PRIVATE,
1326 PUBLIC | PROTECTED)
1327 &&
1328 checkDisjoint(pos, flags,
1329 FINAL,
1330 VOLATILE)
1331 &&
1332 (sym.kind == TYP ||
1333 checkDisjoint(pos, flags,
1334 ABSTRACT | NATIVE,
1335 STRICTFP))
1336 && checkDisjoint(pos, flags,
1337 FINAL,
1338 SEALED | NON_SEALED)
1339 && checkDisjoint(pos, flags,
1340 SEALED,
1341 FINAL | NON_SEALED)
1342 && checkDisjoint(pos, flags,
1343 SEALED,
1344 ANNOTATION)) {
1345 // skip
1346 }
1347 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1348 }
1349
1350 private void warnOnExplicitStrictfp(DiagnosticPosition pos) {
1351 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(pos);
1352 try {
1353 deferredLintHandler.report(_l -> {
1354 if (lint.isEnabled(LintCategory.STRICTFP)) {
1355 log.warning(LintCategory.STRICTFP,
1356 pos, Warnings.Strictfp); }
1357 });
1358 } finally {
1359 deferredLintHandler.setPos(prevLintPos);
1360 }
1361 }
1362
1363
1364 /** Determine if this enum should be implicitly final.
2141 return true;
2142 }
2143 }
2144 }
2145 return false;
2146 }
2147
2148 /** Check that a given method conforms with any method it overrides.
2149 * @param tree The tree from which positions are extracted
2150 * for errors.
2151 * @param m The overriding method.
2152 */
2153 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2154 ClassSymbol origin = (ClassSymbol)m.owner;
2155 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2156 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2157 log.error(tree.pos(), Errors.EnumNoFinalize);
2158 return;
2159 }
2160 }
2161 if (allowRecords && origin.isRecord()) {
2162 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2163 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2164 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2165 if (recordComponent.isPresent()) {
2166 return;
2167 }
2168 }
2169
2170 for (Type t = origin.type; t.hasTag(CLASS);
2171 t = types.supertype(t)) {
2172 if (t != origin.type) {
2173 checkOverride(tree, t, origin, m);
2174 }
2175 for (Type t2 : types.interfaces(t)) {
2176 checkOverride(tree, t2, origin, m);
2177 }
2178 }
2179
2180 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2569 /** Check that all abstract methods implemented by a class are
2570 * mutually compatible.
2571 * @param pos Position to be used for error reporting.
2572 * @param c The class whose interfaces are checked.
2573 */
2574 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2575 List<Type> supertypes = types.interfaces(c);
2576 Type supertype = types.supertype(c);
2577 if (supertype.hasTag(CLASS) &&
2578 (supertype.tsym.flags() & ABSTRACT) != 0)
2579 supertypes = supertypes.prepend(supertype);
2580 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2581 if (!l.head.getTypeArguments().isEmpty() &&
2582 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2583 return;
2584 for (List<Type> m = supertypes; m != l; m = m.tail)
2585 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2586 return;
2587 }
2588 checkCompatibleConcretes(pos, c);
2589 }
2590
2591 /** Check that all non-override equivalent methods accessible from 'site'
2592 * are mutually compatible (JLS 8.4.8/9.4.1).
2593 *
2594 * @param pos Position to be used for error reporting.
2595 * @param site The class whose methods are checked.
2596 * @param sym The method symbol to be checked.
2597 */
2598 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2599 ClashFilter cf = new ClashFilter(site);
2600 //for each method m1 that is overridden (directly or indirectly)
2601 //by method 'sym' in 'site'...
2602
2603 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2604 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2605 for (Symbol m1 : symbolsByName) {
2606 if (!sym.overrides(m1, site.tsym, types, false)) {
2607 continue;
2608 }
4881 }
4882 } else {
4883 Assert.error("Unknown pattern: " + currentPattern.getTag());
4884 }
4885 return false;
4886 }
4887
4888 /** check if a type is a subtype of Externalizable, if that is available. */
4889 boolean isExternalizable(Type t) {
4890 try {
4891 syms.externalizableType.complete();
4892 } catch (CompletionFailure e) {
4893 return false;
4894 }
4895 return types.isSubtype(t, syms.externalizableType);
4896 }
4897
4898 /**
4899 * Check structure of serialization declarations.
4900 */
4901 public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
4902 (new SerialTypeVisitor()).visit(c, tree);
4903 }
4904
4905 /**
4906 * This visitor will warn if a serialization-related field or
4907 * method is declared in a suspicious or incorrect way. In
4908 * particular, it will warn for cases where the runtime
4909 * serialization mechanism will silently ignore a mis-declared
4910 * entity.
4911 *
4912 * Distinguished serialization-related fields and methods:
4913 *
4914 * Methods:
4915 *
4916 * private void writeObject(ObjectOutputStream stream) throws IOException
4917 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4918 *
4919 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4920 * private void readObjectNoData() throws ObjectStreamException
4921 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4922 *
4923 * Fields:
4924 *
4925 * private static final long serialVersionUID
4926 * private static final ObjectStreamField[] serialPersistentFields
4927 *
4928 * Externalizable: methods defined on the interface
4929 * public void writeExternal(ObjectOutput) throws IOException
4930 * public void readExternal(ObjectInput) throws IOException
4931 */
4932 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4933 SerialTypeVisitor() {
4934 this.lint = Check.this.lint;
4935 }
4936
4937 private static final Set<String> serialMethodNames =
4938 Set.of("writeObject", "writeReplace",
4939 "readObject", "readObjectNoData",
4940 "readResolve");
4941
4942 private static final Set<String> serialFieldNames =
4943 Set.of("serialVersionUID", "serialPersistentFields");
4944
4945 // Type of serialPersistentFields
4946 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
4947
4948 Lint lint;
4949
4950 @Override
4951 public Void defaultAction(Element e, JCClassDecl p) {
4952 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
4953 }
4954
4974 if (sym.kind == VAR) {
4975 svuidSym = (VarSymbol)sym;
4976 break;
4977 }
4978 }
4979
4980 if (svuidSym == null) {
4981 log.warning(LintCategory.SERIAL, p.pos(), Warnings.MissingSVUID(c));
4982 }
4983
4984 // Check for serialPersistentFields to gate checks for
4985 // non-serializable non-transient instance fields
4986 boolean serialPersistentFieldsPresent =
4987 c.members()
4988 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
4989 .iterator()
4990 .hasNext();
4991
4992 // Check declarations of serialization-related methods and
4993 // fields
4994 for(Symbol el : c.getEnclosedElements()) {
4995 runUnderLint(el, p, (enclosed, tree) -> {
4996 String name = null;
4997 switch(enclosed.getKind()) {
4998 case FIELD -> {
4999 if (!serialPersistentFieldsPresent) {
5000 var flags = enclosed.flags();
5001 if ( ((flags & TRANSIENT) == 0) &&
5002 ((flags & STATIC) == 0)) {
5003 Type varType = enclosed.asType();
5004 if (!canBeSerialized(varType)) {
5005 // Note per JLS arrays are
5006 // serializable even if the
5007 // component type is not.
5008 log.warning(LintCategory.SERIAL,
5009 TreeInfo.diagnosticPositionFor(enclosed, tree),
5010 Warnings.NonSerializableInstanceField);
5011 } else if (varType.hasTag(ARRAY)) {
5012 ArrayType arrayType = (ArrayType)varType;
5013 Type elementType = arrayType.elemtype;
5048 // will also pull in default methods from
5049 // superinterfaces. In other words, the runtime checks
5050 // (which long predate default methods on interfaces)
5051 // do not admit the possibility of inheriting methods
5052 // this way, a difference from general inheritance.
5053
5054 // The current implementation just checks the enclosed
5055 // elements and does not directly check the inherited
5056 // methods. If all the types are being checked this is
5057 // less of a concern; however, there are cases that
5058 // could be missed. In particular, readResolve and
5059 // writeReplace could, in principle, by inherited from
5060 // a non-serializable superclass and thus not checked
5061 // even if compiled with a serializable child class.
5062 case METHOD -> {
5063 var method = (MethodSymbol)enclosed;
5064 name = method.getSimpleName().toString();
5065 if (serialMethodNames.contains(name)) {
5066 switch (name) {
5067 case "writeObject" -> checkWriteObject(tree, e, method);
5068 case "writeReplace" -> checkWriteReplace(tree,e, method);
5069 case "readObject" -> checkReadObject(tree,e, method);
5070 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5071 case "readResolve" -> checkReadResolve(tree, e, method);
5072 default -> throw new AssertionError();
5073 }
5074 }
5075 }
5076 }
5077 });
5078 }
5079
5080 return null;
5081 }
5082
5083 boolean canBeSerialized(Type type) {
5084 return type.isPrimitive() || rs.isSerializable(type);
5085 }
5086
5087 /**
5088 * Check that Externalizable class needs a public no-arg
5089 * constructor.
5090 *
5091 * Check that a Serializable class has access to the no-arg
5092 * constructor of its first nonserializable superclass.
5093 */
5094 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5095 if (isExternalizable(c.type)) {
5096 for(var sym : c.getEnclosedElements()) {
5097 if (sym.isConstructor() &&
5098 ((sym.flags() & PUBLIC) == PUBLIC)) {
5099 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5100 return;
5101 }
5102 }
5103 }
5104 log.warning(LintCategory.SERIAL, tree.pos(),
5105 Warnings.ExternalizableMissingPublicNoArgCtor);
5106 } else {
5190 // Warn if serialPersistentFields is initialized to a
5191 // literal null.
5192 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5193 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5194 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5195 JCExpression initExpr = variableDef.init;
5196 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5197 log.warning(LintCategory.SERIAL, initExpr.pos(),
5198 Warnings.SPFNullInit);
5199 }
5200 }
5201 }
5202
5203 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5204 // The "synchronized" modifier is seen in the wild on
5205 // readObject and writeObject methods and is generally
5206 // innocuous.
5207
5208 // private void writeObject(ObjectOutputStream stream) throws IOException
5209 checkPrivateNonStaticMethod(tree, method);
5210 checkReturnType(tree, e, method, syms.voidType);
5211 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5212 checkExceptions(tree, e, method, syms.ioExceptionType);
5213 checkExternalizable(tree, e, method);
5214 }
5215
5216 private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
5217 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5218 // ObjectStreamException
5219
5220 // Excluding abstract, could have a more complicated
5221 // rule based on abstract-ness of the class
5222 checkConcreteInstanceMethod(tree, e, method);
5223 checkReturnType(tree, e, method, syms.objectType);
5224 checkNoArgs(tree, e, method);
5225 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5226 }
5227
5228 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5229 // The "synchronized" modifier is seen in the wild on
5230 // readObject and writeObject methods and is generally
5231 // innocuous.
5232
5233 // private void readObject(ObjectInputStream stream)
5234 // throws IOException, ClassNotFoundException
5235 checkPrivateNonStaticMethod(tree, method);
5236 checkReturnType(tree, e, method, syms.voidType);
5237 checkOneArg(tree, e, method, syms.objectInputStreamType);
5238 checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
5239 checkExternalizable(tree, e, method);
5240 }
5241
5242 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5243 // private void readObjectNoData() throws ObjectStreamException
5244 checkPrivateNonStaticMethod(tree, method);
5245 checkReturnType(tree, e, method, syms.voidType);
5246 checkNoArgs(tree, e, method);
5247 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5248 checkExternalizable(tree, e, method);
5249 }
5250
5251 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5252 // ANY-ACCESS-MODIFIER Object readResolve()
5253 // throws ObjectStreamException
5254
5255 // Excluding abstract, could have a more complicated
5256 // rule based on abstract-ness of the class
5257 checkConcreteInstanceMethod(tree, e, method);
5258 checkReturnType(tree,e, method, syms.objectType);
5259 checkNoArgs(tree, e, method);
5260 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5261 }
5262
5263 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5264 //public void writeExternal(ObjectOutput) throws IOException
5265 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5266 }
5267
5268 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5269 // public void readExternal(ObjectInput) throws IOException
5270 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5271 }
5272
5273 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5274 boolean isExtern) {
5275 if (isExtern && isExternMethod(tree, e, method, argType)) {
5276 log.warning(LintCategory.SERIAL,
5277 TreeInfo.diagnosticPositionFor(method, tree),
5278 Warnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5279 }
5280 }
5490 case FIELD -> {
5491 var field = (VarSymbol)enclosed;
5492 switch(name) {
5493 case "serialPersistentFields" -> {
5494 log.warning(LintCategory.SERIAL,
5495 TreeInfo.diagnosticPositionFor(field, tree),
5496 Warnings.IneffectualSerialFieldRecord);
5497 }
5498
5499 case "serialVersionUID" -> {
5500 // Could generate additional warning that
5501 // svuid value is not checked to match for
5502 // records.
5503 checkSerialVersionUID(tree, e, field);
5504 }}
5505 }
5506
5507 case METHOD -> {
5508 var method = (MethodSymbol)enclosed;
5509 switch(name) {
5510 case "writeReplace" -> checkWriteReplace(tree, e, method);
5511 case "readResolve" -> checkReadResolve(tree, e, method);
5512
5513 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5514 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5515
5516 default -> {
5517 if (serialMethodNames.contains(name)) {
5518 log.warning(LintCategory.SERIAL,
5519 TreeInfo.diagnosticPositionFor(method, tree),
5520 Warnings.IneffectualSerialMethodRecord(name));
5521 }
5522 }}
5523 }}});
5524 }
5525 return null;
5526 }
5527
5528 void checkConcreteInstanceMethod(JCClassDecl tree,
5529 Element enclosing,
5530 MethodSymbol method) {
5531 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5532 log.warning(LintCategory.SERIAL,
5533 TreeInfo.diagnosticPositionFor(method, tree),
5534 Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5535 }
5536 }
5537
5538 private void checkReturnType(JCClassDecl tree,
5539 Element enclosing,
5540 MethodSymbol method,
5541 Type expectedReturnType) {
5542 // Note: there may be complications checking writeReplace
5543 // and readResolve since they return Object and could, in
5544 // principle, have covariant overrides and any synthetic
5545 // bridge method would not be represented here for
5546 // checking.
5547 Type rtype = method.getReturnType();
5548 if (!types.isSameType(expectedReturnType, rtype)) {
5549 log.warning(LintCategory.SERIAL,
5550 TreeInfo.diagnosticPositionFor(method, tree),
5551 Warnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5552 rtype, expectedReturnType));
5553 }
5554 }
5555
5556 private void checkOneArg(JCClassDecl tree,
5557 Element enclosing,
5558 MethodSymbol method,
5559 Type expectedType) {
5560 String name = method.getSimpleName().toString();
5561
5562 var parameters= method.getParameters();
5563
5564 if (parameters.size() != 1) {
5565 log.warning(LintCategory.SERIAL,
5566 TreeInfo.diagnosticPositionFor(method, tree),
5567 Warnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5568 return;
5569 }
5570
5571 Type parameterType = parameters.get(0).asType();
5572 if (!types.isSameType(parameterType, expectedType)) {
5573 log.warning(LintCategory.SERIAL,
5574 TreeInfo.diagnosticPositionFor(method, tree),
5575 Warnings.SerialMethodParameterType(method.getSimpleName(),
5576 expectedType,
5577 parameterType));
5578 }
5579 }
5580
5581 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5582 Element enclosing,
5583 MethodSymbol method,
5584 Type expectedType) {
5585 var parameters = method.getParameters();
5586 return (parameters.size() == 1) &&
5587 types.isSameType(parameters.get(0).asType(), expectedType);
5588 }
5589
5590
5591 private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5592 var parameters = method.getParameters();
5593 if (!parameters.isEmpty()) {
5594 log.warning(LintCategory.SERIAL,
5595 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5596 Warnings.SerialMethodNoArgs(method.getSimpleName()));
5597 }
5598 }
5599
5600 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5601 // If the enclosing class is externalizable, warn for the method
5602 if (isExternalizable((Type)enclosing.asType())) {
5603 log.warning(LintCategory.SERIAL,
5604 TreeInfo.diagnosticPositionFor(method, tree),
5605 Warnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5606 }
5607 return;
5608 }
5609
5610 private void checkExceptions(JCClassDecl tree,
5611 Element enclosing,
5612 MethodSymbol method,
5613 Type... declaredExceptions) {
5614 for (Type thrownType: method.getThrownTypes()) {
5615 // For each exception in the throws clause of the
5616 // method, if not an Error and not a RuntimeException,
5617 // check if the exception is a subtype of a declared
5618 // exception from the throws clause of the
5619 // serialization method in question.
5620 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5621 types.isSubtype(thrownType, syms.errorType) ) {
5622 continue;
5623 } else {
5624 boolean declared = false;
5625 for (Type declaredException : declaredExceptions) {
5626 if (types.isSubtype(thrownType, declaredException)) {
5627 declared = true;
5628 continue;
5629 }
5630 }
5631 if (!declared) {
5632 log.warning(LintCategory.SERIAL,
5633 TreeInfo.diagnosticPositionFor(method, tree),
5634 Warnings.SerialMethodUnexpectedException(method.getSimpleName(),
5635 thrownType));
5636 }
5637 }
5638 }
5639 return;
5640 }
5641
5642 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5643 Lint prevLint = lint;
5644 try {
5645 lint = lint.augment((Symbol) symbol);
5646
5647 if (lint.isEnabled(LintCategory.SERIAL)) {
5648 task.accept(symbol, p);
5649 }
5650
5651 return null;
5652 } finally {
5653 lint = prevLint;
5654 }
5655 }
5656
5657 }
5658
5659 }
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.javac.comp;
27
28 import java.util.*;
29 import java.util.function.BiConsumer;
30 import java.util.function.BiPredicate;
31 import java.util.function.Predicate;
32 import java.util.function.Supplier;
33 import java.util.function.ToIntBiFunction;
34 import java.util.stream.Collectors;
35 import java.util.stream.StreamSupport;
36
37 import javax.lang.model.element.ElementKind;
38 import javax.lang.model.element.NestingKind;
39 import javax.tools.JavaFileManager;
40
41 import com.sun.source.tree.CaseTree;
42 import com.sun.tools.javac.code.*;
43 import com.sun.tools.javac.code.Attribute.Compound;
44 import com.sun.tools.javac.code.Directive.ExportsDirective;
45 import com.sun.tools.javac.code.Directive.RequiresDirective;
46 import com.sun.tools.javac.code.Source.Feature;
47 import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata;
48 import com.sun.tools.javac.jvm.*;
49 import com.sun.tools.javac.resources.CompilerProperties.Errors;
50 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
61 import com.sun.tools.javac.code.Lint;
62 import com.sun.tools.javac.code.Lint.LintCategory;
63 import com.sun.tools.javac.code.Scope.WriteableScope;
64 import com.sun.tools.javac.code.Type.*;
65 import com.sun.tools.javac.code.Symbol.*;
66 import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
67 import com.sun.tools.javac.tree.JCTree.*;
68
69 import static com.sun.tools.javac.code.Flags.*;
70 import static com.sun.tools.javac.code.Flags.ANNOTATION;
71 import static com.sun.tools.javac.code.Flags.SYNCHRONIZED;
72 import static com.sun.tools.javac.code.Kinds.*;
73 import static com.sun.tools.javac.code.Kinds.Kind.*;
74 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
75 import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE;
76 import static com.sun.tools.javac.code.TypeTag.*;
77 import static com.sun.tools.javac.code.TypeTag.WILDCARD;
78
79 import static com.sun.tools.javac.tree.JCTree.Tag.*;
80 import javax.lang.model.element.Element;
81 import javax.lang.model.element.TypeElement;
82 import javax.lang.model.type.DeclaredType;
83 import javax.lang.model.util.ElementKindVisitor14;
84
85 /** Type checking helper class for the attribution phase.
86 *
87 * <p><b>This is NOT part of any supported API.
88 * If you write code that depends on this, you do so at your own risk.
89 * This code and its internal interfaces are subject to change or
90 * deletion without notice.</b>
91 */
92 public class Check {
93 protected static final Context.Key<Check> checkKey = new Context.Key<>();
94
95 // Flag bits indicating which item(s) chosen from a pair of items
96 private static final int FIRST = 0x01;
97 private static final int SECOND = 0x02;
98
99 private final Names names;
100 private final Log log;
101 private final Resolve rs;
102 private final Symtab syms;
163
164 boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION);
165 boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL);
166 boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED);
167 boolean enforceMandatoryWarnings = true;
168
169 deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated,
170 enforceMandatoryWarnings, "deprecated", LintCategory.DEPRECATION);
171 removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval,
172 enforceMandatoryWarnings, "removal", LintCategory.REMOVAL);
173 uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked,
174 enforceMandatoryWarnings, "unchecked", LintCategory.UNCHECKED);
175 sunApiHandler = new MandatoryWarningHandler(log, null, false,
176 enforceMandatoryWarnings, "sunapi", null);
177
178 deferredLintHandler = DeferredLintHandler.instance(context);
179
180 allowModules = Feature.MODULES.allowedInSource(source);
181 allowRecords = Feature.RECORDS.allowedInSource(source);
182 allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
183 allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
184 Feature.VALUE_CLASSES.allowedInSource(source);
185 }
186
187 /** Character for synthetic names
188 */
189 char syntheticNameChar;
190
191 /** A table mapping flat names of all compiled classes for each module in this run
192 * to their symbols; maintained from outside.
193 */
194 private Map<Pair<ModuleSymbol, Name>,ClassSymbol> compiled = new HashMap<>();
195
196 /** A handler for messages about deprecated usage.
197 */
198 private MandatoryWarningHandler deprecationHandler;
199
200 /** A handler for messages about deprecated-for-removal usage.
201 */
202 private MandatoryWarningHandler removalHandler;
203
204 /** A handler for messages about unchecked or unsafe usage.
208 /** A handler for messages about using proprietary API.
209 */
210 private MandatoryWarningHandler sunApiHandler;
211
212 /** A handler for deferred lint warnings.
213 */
214 private DeferredLintHandler deferredLintHandler;
215
216 /** Are modules allowed
217 */
218 private final boolean allowModules;
219
220 /** Are records allowed
221 */
222 private final boolean allowRecords;
223
224 /** Are sealed classes allowed
225 */
226 private final boolean allowSealed;
227
228 /** Are value classes allowed
229 */
230 private final boolean allowValueClasses;
231
232 /* *************************************************************************
233 * Errors and Warnings
234 **************************************************************************/
235
236 Lint setLint(Lint newLint) {
237 Lint prev = lint;
238 lint = newLint;
239 return prev;
240 }
241
242 MethodSymbol setMethod(MethodSymbol newMethod) {
243 MethodSymbol prev = method;
244 method = newMethod;
245 return prev;
246 }
247
248 /** Warn about deprecated symbol.
249 * @param pos Position to be used for error reporting.
250 * @param sym The deprecated symbol.
251 */
745 /** Check that type is a class or interface type.
746 * @param pos Position to be used for error reporting.
747 * @param t The type to be checked.
748 */
749 Type checkClassType(DiagnosticPosition pos, Type t) {
750 if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
751 return typeTagError(pos,
752 diags.fragment(Fragments.TypeReqClass),
753 asTypeParam(t));
754 } else {
755 return t;
756 }
757 }
758 //where
759 private Object asTypeParam(Type t) {
760 return (t.hasTag(TYPEVAR))
761 ? diags.fragment(Fragments.TypeParameter(t))
762 : t;
763 }
764
765 void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
766 DiagnosticPosition pos = tree.pos();
767 for (Type st : types.closure(c.type)) {
768 if (st == null || st.tsym == null || st.tsym.kind == ERR)
769 continue;
770 if (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
771 continue;
772 if (!st.tsym.isAbstract()) {
773 if (c != st.tsym) {
774 log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
775 }
776 continue;
777 }
778 // dealing with an abstract value or value super class below.
779 for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
780 if (s.kind == MTH) {
781 if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
782 log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
783 }
784 break;
785 }
786 }
787 }
788 }
789
790 /** Check that type is a valid qualifier for a constructor reference expression
791 */
792 Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
793 t = checkClassOrArrayType(pos, t);
794 if (t.hasTag(CLASS)) {
795 if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
796 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
797 t = types.createErrorType(t);
798 } else if ((t.tsym.flags() & ENUM) != 0) {
799 log.error(pos, Errors.EnumCantBeInstantiated);
800 t = types.createErrorType(t);
801 } else {
802 t = checkClassType(pos, t, true);
803 }
804 } else if (t.hasTag(ARRAY)) {
805 if (!types.isReifiable(((ArrayType)t).elemtype)) {
806 log.error(pos, Errors.GenericArrayCreation);
807 t = types.createErrorType(t);
808 }
809 }
827 args = args.tail;
828 }
829 }
830 return t;
831 }
832
833 /** Check that type is a reference type, i.e. a class, interface or array type
834 * or a type variable.
835 * @param pos Position to be used for error reporting.
836 * @param t The type to be checked.
837 */
838 Type checkRefType(DiagnosticPosition pos, Type t) {
839 if (t.isReference())
840 return t;
841 else
842 return typeTagError(pos,
843 diags.fragment(Fragments.TypeReqRef),
844 t);
845 }
846
847 /** Check that type is an identity type, i.e. not a value type.
848 * When not discernible statically, give it the benefit of doubt
849 * and defer to runtime.
850 *
851 * @param pos Position to be used for error reporting.
852 * @param t The type to be checked.
853 */
854 boolean checkIdentityType(DiagnosticPosition pos, Type t) {
855 if (t.hasTag(TYPEVAR)) {
856 t = types.skipTypeVars(t, false);
857 }
858 if (t.isIntersection()) {
859 IntersectionClassType ict = (IntersectionClassType)t;
860 boolean result = true;
861 for (Type component : ict.getExplicitComponents()) {
862 result &= checkIdentityType(pos, component);
863 }
864 return result;
865 }
866 if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
867 typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
868 return false;
869 }
870 return true;
871 }
872
873 /** Check that each type is a reference type, i.e. a class, interface or array type
874 * or a type variable.
875 * @param trees Original trees, used for error reporting.
876 * @param types The types to be checked.
877 */
878 List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
879 List<JCExpression> tl = trees;
880 for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
881 l.head = checkRefType(tl.head.pos(), l.head);
882 tl = tl.tail;
883 }
884 return types;
885 }
886
887 /** Check that type is a null or reference type.
888 * @param pos Position to be used for error reporting.
889 * @param t The type to be checked.
890 */
891 Type checkNullOrRefType(DiagnosticPosition pos, Type t) {
892 if (t.isReference() || t.hasTag(BOT))
1243 * return modifiers together with any implicit modifiers for that symbol.
1244 * Warning: we can't use flags() here since this method
1245 * is called during class enter, when flags() would cause a premature
1246 * completion.
1247 * @param pos Position to be used for error reporting.
1248 * @param flags The set of modifiers given in a definition.
1249 * @param sym The defined symbol.
1250 */
1251 long checkFlags(DiagnosticPosition pos, long flags, Symbol sym, JCTree tree) {
1252 long mask;
1253 long implicit = 0;
1254
1255 switch (sym.kind) {
1256 case VAR:
1257 if (TreeInfo.isReceiverParam(tree))
1258 mask = ReceiverParamFlags;
1259 else if (sym.owner.kind != TYP)
1260 mask = LocalVarFlags;
1261 else if ((sym.owner.flags_field & INTERFACE) != 0)
1262 mask = implicit = InterfaceVarFlags;
1263 else {
1264 boolean isInstanceFieldOfValueClass = sym.owner.type.isValueClass() && (flags & STATIC) == 0;
1265 mask = !isInstanceFieldOfValueClass ? VarFlags : ValueFieldFlags;
1266 if (isInstanceFieldOfValueClass) {
1267 implicit |= FINAL | STRICT;
1268 }
1269 }
1270 break;
1271 case MTH:
1272 if (sym.name == names.init) {
1273 if ((sym.owner.flags_field & ENUM) != 0) {
1274 // enum constructors cannot be declared public or
1275 // protected and must be implicitly or explicitly
1276 // private
1277 implicit = PRIVATE;
1278 mask = PRIVATE;
1279 } else
1280 mask = ConstructorFlags;
1281 } else if ((sym.owner.flags_field & INTERFACE) != 0) {
1282 if ((sym.owner.flags_field & ANNOTATION) != 0) {
1283 mask = AnnotationTypeElementMask;
1284 implicit = PUBLIC | ABSTRACT;
1285 } else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
1286 mask = InterfaceMethodMask;
1287 implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
1288 if ((flags & DEFAULT) != 0) {
1289 implicit |= ABSTRACT;
1290 }
1291 } else {
1292 mask = implicit = InterfaceMethodFlags;
1293 }
1294 } else if ((sym.owner.flags_field & RECORD) != 0) {
1295 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1296 RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
1297 } else {
1298 // value objects do not have an associated monitor/lock
1299 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
1300 MethodFlags & ~SYNCHRONIZED : MethodFlags;
1301 }
1302 if ((flags & STRICTFP) != 0) {
1303 warnOnExplicitStrictfp(pos);
1304 }
1305 // Imply STRICTFP if owner has STRICTFP set.
1306 if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
1307 ((flags) & Flags.DEFAULT) != 0)
1308 implicit |= sym.owner.flags_field & STRICTFP;
1309 break;
1310 case TYP:
1311 if (sym.owner.kind.matches(KindSelector.VAL_MTH) ||
1312 (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
1313 boolean implicitlyStatic = !sym.isAnonymous() &&
1314 ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
1315 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
1316 // local statics are allowed only if records are allowed too
1317 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
1318 implicit = implicitlyStatic ? STATIC : implicit;
1319 } else if (sym.owner.kind == TYP) {
1320 // statics in inner classes are allowed only if records are allowed too
1321 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
1322 if (sym.owner.owner.kind == PCK ||
1323 (sym.owner.flags_field & STATIC) != 0) {
1324 mask |= STATIC;
1325 } else if (!allowRecords && ((flags & ENUM) != 0 || (flags & RECORD) != 0)) {
1326 log.error(pos, Errors.StaticDeclarationNotAllowedInInnerClasses);
1327 }
1328 // Nested interfaces and enums are always STATIC (Spec ???)
1329 if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) implicit = STATIC;
1330 } else {
1331 mask = ExtendedClassFlags;
1332 }
1333 if ((flags & (VALUE_CLASS | SEALED | ABSTRACT)) == (VALUE_CLASS | SEALED) ||
1334 (flags & (VALUE_CLASS | NON_SEALED | ABSTRACT)) == (VALUE_CLASS | NON_SEALED)) {
1335 log.error(pos, Errors.NonAbstractValueClassCantBeSealedOrNonSealed);
1336 }
1337 // Interfaces are always ABSTRACT
1338 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1339
1340 if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
1341 implicit |= IDENTITY_TYPE;
1342 }
1343
1344 if ((flags & ENUM) != 0) {
1345 // enums can't be declared abstract, final, sealed or non-sealed or value
1346 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
1347 implicit |= implicitEnumFinalFlag(tree);
1348 }
1349 if ((flags & RECORD) != 0) {
1350 // records can't be declared abstract
1351 mask &= ~ABSTRACT;
1352 implicit |= FINAL;
1353 }
1354 if ((flags & STRICTFP) != 0) {
1355 warnOnExplicitStrictfp(pos);
1356 }
1357 // Imply STRICTFP if owner has STRICTFP set.
1358 implicit |= sym.owner.flags_field & STRICTFP;
1359
1360 // concrete value classes are implicitly final
1361 if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
1362 implicit |= FINAL;
1363 }
1364 break;
1365 default:
1366 throw new AssertionError();
1367 }
1368 long illegal = flags & ExtendedStandardFlags & ~mask;
1369 if (illegal != 0) {
1370 if ((illegal & INTERFACE) != 0) {
1371 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1372 mask |= INTERFACE;
1373 }
1374 else {
1375 log.error(pos,
1376 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1377 }
1378 } else if ((sym.kind == TYP ||
1379 // ISSUE: Disallowing abstract&private is no longer appropriate
1380 // in the presence of inner classes. Should it be deleted here?
1381 checkDisjoint(pos, flags,
1382 ABSTRACT,
1383 PRIVATE | STATIC | DEFAULT))
1384 &&
1385 checkDisjoint(pos, flags,
1386 STATIC | PRIVATE,
1387 DEFAULT)
1388 &&
1389 checkDisjoint(pos, flags,
1390 ABSTRACT | INTERFACE,
1391 FINAL | NATIVE | SYNCHRONIZED)
1392 &&
1393 checkDisjoint(pos, flags,
1394 PUBLIC,
1395 PRIVATE | PROTECTED)
1396 &&
1397 checkDisjoint(pos, flags,
1398 PRIVATE,
1399 PUBLIC | PROTECTED)
1400 &&
1401 // we are using `implicit` here as instance fields of value classes are implicitly final
1402 checkDisjoint(pos, flags | implicit,
1403 FINAL,
1404 VOLATILE)
1405 &&
1406 (sym.kind == TYP ||
1407 checkDisjoint(pos, flags,
1408 ABSTRACT | NATIVE,
1409 STRICTFP))
1410 && checkDisjoint(pos, flags,
1411 FINAL,
1412 SEALED | NON_SEALED)
1413 && checkDisjoint(pos, flags,
1414 SEALED,
1415 FINAL | NON_SEALED)
1416 && checkDisjoint(pos, flags,
1417 SEALED,
1418 ANNOTATION)
1419 && checkDisjoint(pos, flags,
1420 VALUE_CLASS,
1421 ANNOTATION)
1422 && checkDisjoint(pos, flags,
1423 VALUE_CLASS,
1424 INTERFACE) ) {
1425 // skip
1426 }
1427 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1428 }
1429
1430 private void warnOnExplicitStrictfp(DiagnosticPosition pos) {
1431 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(pos);
1432 try {
1433 deferredLintHandler.report(_l -> {
1434 if (lint.isEnabled(LintCategory.STRICTFP)) {
1435 log.warning(LintCategory.STRICTFP,
1436 pos, Warnings.Strictfp); }
1437 });
1438 } finally {
1439 deferredLintHandler.setPos(prevLintPos);
1440 }
1441 }
1442
1443
1444 /** Determine if this enum should be implicitly final.
2221 return true;
2222 }
2223 }
2224 }
2225 return false;
2226 }
2227
2228 /** Check that a given method conforms with any method it overrides.
2229 * @param tree The tree from which positions are extracted
2230 * for errors.
2231 * @param m The overriding method.
2232 */
2233 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2234 ClassSymbol origin = (ClassSymbol)m.owner;
2235 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2236 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2237 log.error(tree.pos(), Errors.EnumNoFinalize);
2238 return;
2239 }
2240 }
2241 if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
2242 if (m.overrides(syms.objectFinalize, origin, types, false)) {
2243 log.warning(tree.pos(), Warnings.ValueFinalize);
2244 }
2245 }
2246 if (allowRecords && origin.isRecord()) {
2247 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2248 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2249 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2250 if (recordComponent.isPresent()) {
2251 return;
2252 }
2253 }
2254
2255 for (Type t = origin.type; t.hasTag(CLASS);
2256 t = types.supertype(t)) {
2257 if (t != origin.type) {
2258 checkOverride(tree, t, origin, m);
2259 }
2260 for (Type t2 : types.interfaces(t)) {
2261 checkOverride(tree, t2, origin, m);
2262 }
2263 }
2264
2265 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2654 /** Check that all abstract methods implemented by a class are
2655 * mutually compatible.
2656 * @param pos Position to be used for error reporting.
2657 * @param c The class whose interfaces are checked.
2658 */
2659 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2660 List<Type> supertypes = types.interfaces(c);
2661 Type supertype = types.supertype(c);
2662 if (supertype.hasTag(CLASS) &&
2663 (supertype.tsym.flags() & ABSTRACT) != 0)
2664 supertypes = supertypes.prepend(supertype);
2665 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2666 if (!l.head.getTypeArguments().isEmpty() &&
2667 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2668 return;
2669 for (List<Type> m = supertypes; m != l; m = m.tail)
2670 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2671 return;
2672 }
2673 checkCompatibleConcretes(pos, c);
2674
2675 Type identitySuper = null;
2676 for (Type t : types.closure(c)) {
2677 if (t != c) {
2678 if (t.isIdentityClass() && (t.tsym.flags() & VALUE_BASED) == 0)
2679 identitySuper = t;
2680 if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
2681 log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
2682 break;
2683 }
2684 }
2685 }
2686 }
2687
2688 /** Check that all non-override equivalent methods accessible from 'site'
2689 * are mutually compatible (JLS 8.4.8/9.4.1).
2690 *
2691 * @param pos Position to be used for error reporting.
2692 * @param site The class whose methods are checked.
2693 * @param sym The method symbol to be checked.
2694 */
2695 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2696 ClashFilter cf = new ClashFilter(site);
2697 //for each method m1 that is overridden (directly or indirectly)
2698 //by method 'sym' in 'site'...
2699
2700 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2701 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2702 for (Symbol m1 : symbolsByName) {
2703 if (!sym.overrides(m1, site.tsym, types, false)) {
2704 continue;
2705 }
4978 }
4979 } else {
4980 Assert.error("Unknown pattern: " + currentPattern.getTag());
4981 }
4982 return false;
4983 }
4984
4985 /** check if a type is a subtype of Externalizable, if that is available. */
4986 boolean isExternalizable(Type t) {
4987 try {
4988 syms.externalizableType.complete();
4989 } catch (CompletionFailure e) {
4990 return false;
4991 }
4992 return types.isSubtype(t, syms.externalizableType);
4993 }
4994
4995 /**
4996 * Check structure of serialization declarations.
4997 */
4998 public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
4999 (new SerialTypeVisitor(env)).visit(c, tree);
5000 }
5001
5002 /**
5003 * This visitor will warn if a serialization-related field or
5004 * method is declared in a suspicious or incorrect way. In
5005 * particular, it will warn for cases where the runtime
5006 * serialization mechanism will silently ignore a mis-declared
5007 * entity.
5008 *
5009 * Distinguished serialization-related fields and methods:
5010 *
5011 * Methods:
5012 *
5013 * private void writeObject(ObjectOutputStream stream) throws IOException
5014 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
5015 *
5016 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
5017 * private void readObjectNoData() throws ObjectStreamException
5018 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
5019 *
5020 * Fields:
5021 *
5022 * private static final long serialVersionUID
5023 * private static final ObjectStreamField[] serialPersistentFields
5024 *
5025 * Externalizable: methods defined on the interface
5026 * public void writeExternal(ObjectOutput) throws IOException
5027 * public void readExternal(ObjectInput) throws IOException
5028 */
5029 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
5030 Env<AttrContext> env;
5031 SerialTypeVisitor(Env<AttrContext> env) {
5032 this.lint = Check.this.lint;
5033 this.env = env;
5034 }
5035
5036 private static final Set<String> serialMethodNames =
5037 Set.of("writeObject", "writeReplace",
5038 "readObject", "readObjectNoData",
5039 "readResolve");
5040
5041 private static final Set<String> serialFieldNames =
5042 Set.of("serialVersionUID", "serialPersistentFields");
5043
5044 // Type of serialPersistentFields
5045 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
5046
5047 Lint lint;
5048
5049 @Override
5050 public Void defaultAction(Element e, JCClassDecl p) {
5051 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
5052 }
5053
5073 if (sym.kind == VAR) {
5074 svuidSym = (VarSymbol)sym;
5075 break;
5076 }
5077 }
5078
5079 if (svuidSym == null) {
5080 log.warning(LintCategory.SERIAL, p.pos(), Warnings.MissingSVUID(c));
5081 }
5082
5083 // Check for serialPersistentFields to gate checks for
5084 // non-serializable non-transient instance fields
5085 boolean serialPersistentFieldsPresent =
5086 c.members()
5087 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
5088 .iterator()
5089 .hasNext();
5090
5091 // Check declarations of serialization-related methods and
5092 // fields
5093 final boolean[] hasWriteReplace = {false};
5094 for(Symbol el : c.getEnclosedElements()) {
5095 runUnderLint(el, p, (enclosed, tree) -> {
5096 String name = null;
5097 switch(enclosed.getKind()) {
5098 case FIELD -> {
5099 if (!serialPersistentFieldsPresent) {
5100 var flags = enclosed.flags();
5101 if ( ((flags & TRANSIENT) == 0) &&
5102 ((flags & STATIC) == 0)) {
5103 Type varType = enclosed.asType();
5104 if (!canBeSerialized(varType)) {
5105 // Note per JLS arrays are
5106 // serializable even if the
5107 // component type is not.
5108 log.warning(LintCategory.SERIAL,
5109 TreeInfo.diagnosticPositionFor(enclosed, tree),
5110 Warnings.NonSerializableInstanceField);
5111 } else if (varType.hasTag(ARRAY)) {
5112 ArrayType arrayType = (ArrayType)varType;
5113 Type elementType = arrayType.elemtype;
5148 // will also pull in default methods from
5149 // superinterfaces. In other words, the runtime checks
5150 // (which long predate default methods on interfaces)
5151 // do not admit the possibility of inheriting methods
5152 // this way, a difference from general inheritance.
5153
5154 // The current implementation just checks the enclosed
5155 // elements and does not directly check the inherited
5156 // methods. If all the types are being checked this is
5157 // less of a concern; however, there are cases that
5158 // could be missed. In particular, readResolve and
5159 // writeReplace could, in principle, by inherited from
5160 // a non-serializable superclass and thus not checked
5161 // even if compiled with a serializable child class.
5162 case METHOD -> {
5163 var method = (MethodSymbol)enclosed;
5164 name = method.getSimpleName().toString();
5165 if (serialMethodNames.contains(name)) {
5166 switch (name) {
5167 case "writeObject" -> checkWriteObject(tree, e, method);
5168 case "writeReplace" -> {hasWriteReplace[0] = true; hasAppropriateWriteReplace(tree, method, true);}
5169 case "readObject" -> checkReadObject(tree,e, method);
5170 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5171 case "readResolve" -> checkReadResolve(tree, e, method);
5172 default -> throw new AssertionError();
5173 }
5174 }
5175 }
5176 }
5177 });
5178 }
5179 if (!hasWriteReplace[0] &&
5180 (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
5181 !c.isAbstract() && !c.isRecord() &&
5182 types.unboxedType(c.type) == Type.noType) {
5183 // we need to check if the class is inheriting an appropriate writeReplace method
5184 MethodSymbol ms = null;
5185 Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log);
5186 try {
5187 ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
5188 } catch (FatalError fe) {
5189 // ignore no method was found
5190 } finally {
5191 log.popDiagnosticHandler(discardHandler);
5192 }
5193 if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
5194 log.warning(LintCategory.SERIAL, p,
5195 c.isValueClass() ? Warnings.SerializableValueClassWithoutWriteReplace1 :
5196 Warnings.SerializableValueClassWithoutWriteReplace2);
5197 }
5198 }
5199 return null;
5200 }
5201
5202 boolean canBeSerialized(Type type) {
5203 return type.isPrimitive() || rs.isSerializable(type);
5204 }
5205
5206 private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
5207 while (c.getKind() == ElementKind.CLASS) {
5208 Type sup = ((ClassSymbol)c).getSuperclass();
5209 if (!sup.hasTag(CLASS) || sup.isErroneous() ||
5210 sup.tsym == syms.objectType.tsym) {
5211 return false;
5212 }
5213 // if it is a value super class it has to be abstract
5214 if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
5215 return true;
5216 }
5217 c = sup.tsym;
5218 }
5219 return false;
5220 }
5221
5222 /**
5223 * Check that Externalizable class needs a public no-arg
5224 * constructor.
5225 *
5226 * Check that a Serializable class has access to the no-arg
5227 * constructor of its first nonserializable superclass.
5228 */
5229 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5230 if (isExternalizable(c.type)) {
5231 for(var sym : c.getEnclosedElements()) {
5232 if (sym.isConstructor() &&
5233 ((sym.flags() & PUBLIC) == PUBLIC)) {
5234 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5235 return;
5236 }
5237 }
5238 }
5239 log.warning(LintCategory.SERIAL, tree.pos(),
5240 Warnings.ExternalizableMissingPublicNoArgCtor);
5241 } else {
5325 // Warn if serialPersistentFields is initialized to a
5326 // literal null.
5327 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5328 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5329 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5330 JCExpression initExpr = variableDef.init;
5331 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5332 log.warning(LintCategory.SERIAL, initExpr.pos(),
5333 Warnings.SPFNullInit);
5334 }
5335 }
5336 }
5337
5338 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5339 // The "synchronized" modifier is seen in the wild on
5340 // readObject and writeObject methods and is generally
5341 // innocuous.
5342
5343 // private void writeObject(ObjectOutputStream stream) throws IOException
5344 checkPrivateNonStaticMethod(tree, method);
5345 isExpectedReturnType(tree, method, syms.voidType, true);
5346 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5347 hasExpectedExceptions(tree, method, true, syms.ioExceptionType);
5348 checkExternalizable(tree, e, method);
5349 }
5350
5351 private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
5352 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5353 // ObjectStreamException
5354
5355 // Excluding abstract, could have a more complicated
5356 // rule based on abstract-ness of the class
5357 return isConcreteInstanceMethod(tree, method, warn) &&
5358 isExpectedReturnType(tree, method, syms.objectType, warn) &&
5359 hasNoArgs(tree, method, warn) &&
5360 hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
5361 }
5362
5363 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5364 // The "synchronized" modifier is seen in the wild on
5365 // readObject and writeObject methods and is generally
5366 // innocuous.
5367
5368 // private void readObject(ObjectInputStream stream)
5369 // throws IOException, ClassNotFoundException
5370 checkPrivateNonStaticMethod(tree, method);
5371 isExpectedReturnType(tree, method, syms.voidType, true);
5372 checkOneArg(tree, e, method, syms.objectInputStreamType);
5373 hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType);
5374 checkExternalizable(tree, e, method);
5375 }
5376
5377 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5378 // private void readObjectNoData() throws ObjectStreamException
5379 checkPrivateNonStaticMethod(tree, method);
5380 isExpectedReturnType(tree, method, syms.voidType, true);
5381 hasNoArgs(tree, method, true);
5382 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5383 checkExternalizable(tree, e, method);
5384 }
5385
5386 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5387 // ANY-ACCESS-MODIFIER Object readResolve()
5388 // throws ObjectStreamException
5389
5390 // Excluding abstract, could have a more complicated
5391 // rule based on abstract-ness of the class
5392 isConcreteInstanceMethod(tree, method, true);
5393 isExpectedReturnType(tree, method, syms.objectType, true);
5394 hasNoArgs(tree, method, true);
5395 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5396 }
5397
5398 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5399 //public void writeExternal(ObjectOutput) throws IOException
5400 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5401 }
5402
5403 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5404 // public void readExternal(ObjectInput) throws IOException
5405 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5406 }
5407
5408 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5409 boolean isExtern) {
5410 if (isExtern && isExternMethod(tree, e, method, argType)) {
5411 log.warning(LintCategory.SERIAL,
5412 TreeInfo.diagnosticPositionFor(method, tree),
5413 Warnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5414 }
5415 }
5625 case FIELD -> {
5626 var field = (VarSymbol)enclosed;
5627 switch(name) {
5628 case "serialPersistentFields" -> {
5629 log.warning(LintCategory.SERIAL,
5630 TreeInfo.diagnosticPositionFor(field, tree),
5631 Warnings.IneffectualSerialFieldRecord);
5632 }
5633
5634 case "serialVersionUID" -> {
5635 // Could generate additional warning that
5636 // svuid value is not checked to match for
5637 // records.
5638 checkSerialVersionUID(tree, e, field);
5639 }}
5640 }
5641
5642 case METHOD -> {
5643 var method = (MethodSymbol)enclosed;
5644 switch(name) {
5645 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5646 case "readResolve" -> checkReadResolve(tree, e, method);
5647
5648 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5649 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5650
5651 default -> {
5652 if (serialMethodNames.contains(name)) {
5653 log.warning(LintCategory.SERIAL,
5654 TreeInfo.diagnosticPositionFor(method, tree),
5655 Warnings.IneffectualSerialMethodRecord(name));
5656 }
5657 }}
5658 }}});
5659 }
5660 return null;
5661 }
5662
5663 boolean isConcreteInstanceMethod(JCClassDecl tree,
5664 MethodSymbol method,
5665 boolean warn) {
5666 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5667 if (warn) {
5668 log.warning(LintCategory.SERIAL,
5669 TreeInfo.diagnosticPositionFor(method, tree),
5670 Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5671 }
5672 return false;
5673 }
5674 return true;
5675 }
5676
5677 private boolean isExpectedReturnType(JCClassDecl tree,
5678 MethodSymbol method,
5679 Type expectedReturnType,
5680 boolean warn) {
5681 // Note: there may be complications checking writeReplace
5682 // and readResolve since they return Object and could, in
5683 // principle, have covariant overrides and any synthetic
5684 // bridge method would not be represented here for
5685 // checking.
5686 Type rtype = method.getReturnType();
5687 if (!types.isSameType(expectedReturnType, rtype)) {
5688 if (warn) {
5689 log.warning(LintCategory.SERIAL,
5690 TreeInfo.diagnosticPositionFor(method, tree),
5691 Warnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5692 rtype, expectedReturnType));
5693 }
5694 return false;
5695 }
5696 return true;
5697 }
5698
5699 private void checkOneArg(JCClassDecl tree,
5700 Element enclosing,
5701 MethodSymbol method,
5702 Type expectedType) {
5703 String name = method.getSimpleName().toString();
5704
5705 var parameters= method.getParameters();
5706
5707 if (parameters.size() != 1) {
5708 log.warning(LintCategory.SERIAL,
5709 TreeInfo.diagnosticPositionFor(method, tree),
5710 Warnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5711 return;
5712 }
5713
5714 Type parameterType = parameters.get(0).asType();
5715 if (!types.isSameType(parameterType, expectedType)) {
5716 log.warning(LintCategory.SERIAL,
5717 TreeInfo.diagnosticPositionFor(method, tree),
5718 Warnings.SerialMethodParameterType(method.getSimpleName(),
5719 expectedType,
5720 parameterType));
5721 }
5722 }
5723
5724 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5725 Element enclosing,
5726 MethodSymbol method,
5727 Type expectedType) {
5728 var parameters = method.getParameters();
5729 return (parameters.size() == 1) &&
5730 types.isSameType(parameters.get(0).asType(), expectedType);
5731 }
5732
5733
5734 boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
5735 var parameters = method.getParameters();
5736 if (!parameters.isEmpty()) {
5737 if (warn) {
5738 log.warning(LintCategory.SERIAL,
5739 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5740 Warnings.SerialMethodNoArgs(method.getSimpleName()));
5741 }
5742 return false;
5743 }
5744 return true;
5745 }
5746
5747 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5748 // If the enclosing class is externalizable, warn for the method
5749 if (isExternalizable((Type)enclosing.asType())) {
5750 log.warning(LintCategory.SERIAL,
5751 TreeInfo.diagnosticPositionFor(method, tree),
5752 Warnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5753 }
5754 return;
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(LintCategory.SERIAL,
5781 TreeInfo.diagnosticPositionFor(method, tree),
5782 Warnings.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 }
|