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,
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(() -> {
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 }
4043 break;
4044
4045 // super()/this() calls must only appear in a constructor
4046 if (!constructor) {
4047 log.error(apply.pos(), Errors.CallMustOnlyAppearInCtor);
4048 break;
4049 }
4050
4051 // super()/this() calls must be a top level statement
4052 if (scanDepth != MATCH_SCAN_DEPTH) {
4053 log.error(apply.pos(), Errors.CtorCallsNotAllowedHere);
4054 break;
4055 }
4056
4057 // super()/this() calls must not appear more than once
4058 if (initCall != null) {
4059 log.error(apply.pos(), Errors.RedundantSuperclassInit);
4060 break;
4061 }
4062
4063 // If super()/this() isn't first, require "statements before super()" feature
4064 if (!firstStatement)
4065 preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT);
4066
4067 // We found a legitimate super()/this() call; remember it
4068 initCall = methodName;
4069 } while (false);
4070
4071 // Proceed
4072 super.visitApply(apply);
4073 }
4074
4075 @Override
4076 public void visitReturn(JCReturn tree) {
4077 if (constructor && initCall == null && earlyReturn == null)
4078 earlyReturn = tree; // we have seen a return but not (yet) a super()/this()
4079 super.visitReturn(tree);
4080 }
4081
4082 @Override
4083 public void visitClassDef(JCClassDecl tree) {
4904 }
4905 } else {
4906 Assert.error("Unknown pattern: " + currentPattern.getTag());
4907 }
4908 return false;
4909 }
4910
4911 /** check if a type is a subtype of Externalizable, if that is available. */
4912 boolean isExternalizable(Type t) {
4913 try {
4914 syms.externalizableType.complete();
4915 } catch (CompletionFailure e) {
4916 return false;
4917 }
4918 return types.isSubtype(t, syms.externalizableType);
4919 }
4920
4921 /**
4922 * Check structure of serialization declarations.
4923 */
4924 public void checkSerialStructure(JCClassDecl tree, ClassSymbol c) {
4925 (new SerialTypeVisitor()).visit(c, tree);
4926 }
4927
4928 /**
4929 * This visitor will warn if a serialization-related field or
4930 * method is declared in a suspicious or incorrect way. In
4931 * particular, it will warn for cases where the runtime
4932 * serialization mechanism will silently ignore a mis-declared
4933 * entity.
4934 *
4935 * Distinguished serialization-related fields and methods:
4936 *
4937 * Methods:
4938 *
4939 * private void writeObject(ObjectOutputStream stream) throws IOException
4940 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
4941 *
4942 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
4943 * private void readObjectNoData() throws ObjectStreamException
4944 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
4945 *
4946 * Fields:
4947 *
4948 * private static final long serialVersionUID
4949 * private static final ObjectStreamField[] serialPersistentFields
4950 *
4951 * Externalizable: methods defined on the interface
4952 * public void writeExternal(ObjectOutput) throws IOException
4953 * public void readExternal(ObjectInput) throws IOException
4954 */
4955 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
4956 SerialTypeVisitor() {
4957 this.lint = Check.this.lint;
4958 }
4959
4960 private static final Set<String> serialMethodNames =
4961 Set.of("writeObject", "writeReplace",
4962 "readObject", "readObjectNoData",
4963 "readResolve");
4964
4965 private static final Set<String> serialFieldNames =
4966 Set.of("serialVersionUID", "serialPersistentFields");
4967
4968 // Type of serialPersistentFields
4969 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
4970
4971 Lint lint;
4972
4973 @Override
4974 public Void defaultAction(Element e, JCClassDecl p) {
4975 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
4976 }
4977
4997 if (sym.kind == VAR) {
4998 svuidSym = (VarSymbol)sym;
4999 break;
5000 }
5001 }
5002
5003 if (svuidSym == null) {
5004 log.warning(LintCategory.SERIAL, p.pos(), Warnings.MissingSVUID(c));
5005 }
5006
5007 // Check for serialPersistentFields to gate checks for
5008 // non-serializable non-transient instance fields
5009 boolean serialPersistentFieldsPresent =
5010 c.members()
5011 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
5012 .iterator()
5013 .hasNext();
5014
5015 // Check declarations of serialization-related methods and
5016 // fields
5017 for(Symbol el : c.getEnclosedElements()) {
5018 runUnderLint(el, p, (enclosed, tree) -> {
5019 String name = null;
5020 switch(enclosed.getKind()) {
5021 case FIELD -> {
5022 if (!serialPersistentFieldsPresent) {
5023 var flags = enclosed.flags();
5024 if ( ((flags & TRANSIENT) == 0) &&
5025 ((flags & STATIC) == 0)) {
5026 Type varType = enclosed.asType();
5027 if (!canBeSerialized(varType)) {
5028 // Note per JLS arrays are
5029 // serializable even if the
5030 // component type is not.
5031 log.warning(LintCategory.SERIAL,
5032 TreeInfo.diagnosticPositionFor(enclosed, tree),
5033 Warnings.NonSerializableInstanceField);
5034 } else if (varType.hasTag(ARRAY)) {
5035 ArrayType arrayType = (ArrayType)varType;
5036 Type elementType = arrayType.elemtype;
5071 // will also pull in default methods from
5072 // superinterfaces. In other words, the runtime checks
5073 // (which long predate default methods on interfaces)
5074 // do not admit the possibility of inheriting methods
5075 // this way, a difference from general inheritance.
5076
5077 // The current implementation just checks the enclosed
5078 // elements and does not directly check the inherited
5079 // methods. If all the types are being checked this is
5080 // less of a concern; however, there are cases that
5081 // could be missed. In particular, readResolve and
5082 // writeReplace could, in principle, by inherited from
5083 // a non-serializable superclass and thus not checked
5084 // even if compiled with a serializable child class.
5085 case METHOD -> {
5086 var method = (MethodSymbol)enclosed;
5087 name = method.getSimpleName().toString();
5088 if (serialMethodNames.contains(name)) {
5089 switch (name) {
5090 case "writeObject" -> checkWriteObject(tree, e, method);
5091 case "writeReplace" -> checkWriteReplace(tree,e, method);
5092 case "readObject" -> checkReadObject(tree,e, method);
5093 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5094 case "readResolve" -> checkReadResolve(tree, e, method);
5095 default -> throw new AssertionError();
5096 }
5097 }
5098 }
5099 }
5100 });
5101 }
5102
5103 return null;
5104 }
5105
5106 boolean canBeSerialized(Type type) {
5107 return type.isPrimitive() || rs.isSerializable(type);
5108 }
5109
5110 /**
5111 * Check that Externalizable class needs a public no-arg
5112 * constructor.
5113 *
5114 * Check that a Serializable class has access to the no-arg
5115 * constructor of its first nonserializable superclass.
5116 */
5117 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5118 if (isExternalizable(c.type)) {
5119 for(var sym : c.getEnclosedElements()) {
5120 if (sym.isConstructor() &&
5121 ((sym.flags() & PUBLIC) == PUBLIC)) {
5122 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5123 return;
5124 }
5125 }
5126 }
5127 log.warning(LintCategory.SERIAL, tree.pos(),
5128 Warnings.ExternalizableMissingPublicNoArgCtor);
5129 } else {
5213 // Warn if serialPersistentFields is initialized to a
5214 // literal null.
5215 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5216 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5217 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5218 JCExpression initExpr = variableDef.init;
5219 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5220 log.warning(LintCategory.SERIAL, initExpr.pos(),
5221 Warnings.SPFNullInit);
5222 }
5223 }
5224 }
5225
5226 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5227 // The "synchronized" modifier is seen in the wild on
5228 // readObject and writeObject methods and is generally
5229 // innocuous.
5230
5231 // private void writeObject(ObjectOutputStream stream) throws IOException
5232 checkPrivateNonStaticMethod(tree, method);
5233 checkReturnType(tree, e, method, syms.voidType);
5234 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5235 checkExceptions(tree, e, method, syms.ioExceptionType);
5236 checkExternalizable(tree, e, method);
5237 }
5238
5239 private void checkWriteReplace(JCClassDecl tree, Element e, MethodSymbol method) {
5240 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5241 // ObjectStreamException
5242
5243 // Excluding abstract, could have a more complicated
5244 // rule based on abstract-ness of the class
5245 checkConcreteInstanceMethod(tree, e, method);
5246 checkReturnType(tree, e, method, syms.objectType);
5247 checkNoArgs(tree, e, method);
5248 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5249 }
5250
5251 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5252 // The "synchronized" modifier is seen in the wild on
5253 // readObject and writeObject methods and is generally
5254 // innocuous.
5255
5256 // private void readObject(ObjectInputStream stream)
5257 // throws IOException, ClassNotFoundException
5258 checkPrivateNonStaticMethod(tree, method);
5259 checkReturnType(tree, e, method, syms.voidType);
5260 checkOneArg(tree, e, method, syms.objectInputStreamType);
5261 checkExceptions(tree, e, method, syms.ioExceptionType, syms.classNotFoundExceptionType);
5262 checkExternalizable(tree, e, method);
5263 }
5264
5265 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5266 // private void readObjectNoData() throws ObjectStreamException
5267 checkPrivateNonStaticMethod(tree, method);
5268 checkReturnType(tree, e, method, syms.voidType);
5269 checkNoArgs(tree, e, method);
5270 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5271 checkExternalizable(tree, e, method);
5272 }
5273
5274 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5275 // ANY-ACCESS-MODIFIER Object readResolve()
5276 // throws ObjectStreamException
5277
5278 // Excluding abstract, could have a more complicated
5279 // rule based on abstract-ness of the class
5280 checkConcreteInstanceMethod(tree, e, method);
5281 checkReturnType(tree,e, method, syms.objectType);
5282 checkNoArgs(tree, e, method);
5283 checkExceptions(tree, e, method, syms.objectStreamExceptionType);
5284 }
5285
5286 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5287 //public void writeExternal(ObjectOutput) throws IOException
5288 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5289 }
5290
5291 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5292 // public void readExternal(ObjectInput) throws IOException
5293 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5294 }
5295
5296 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5297 boolean isExtern) {
5298 if (isExtern && isExternMethod(tree, e, method, argType)) {
5299 log.warning(LintCategory.SERIAL,
5300 TreeInfo.diagnosticPositionFor(method, tree),
5301 Warnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5302 }
5303 }
5513 case FIELD -> {
5514 var field = (VarSymbol)enclosed;
5515 switch(name) {
5516 case "serialPersistentFields" -> {
5517 log.warning(LintCategory.SERIAL,
5518 TreeInfo.diagnosticPositionFor(field, tree),
5519 Warnings.IneffectualSerialFieldRecord);
5520 }
5521
5522 case "serialVersionUID" -> {
5523 // Could generate additional warning that
5524 // svuid value is not checked to match for
5525 // records.
5526 checkSerialVersionUID(tree, e, field);
5527 }}
5528 }
5529
5530 case METHOD -> {
5531 var method = (MethodSymbol)enclosed;
5532 switch(name) {
5533 case "writeReplace" -> checkWriteReplace(tree, e, method);
5534 case "readResolve" -> checkReadResolve(tree, e, method);
5535
5536 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5537 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5538
5539 default -> {
5540 if (serialMethodNames.contains(name)) {
5541 log.warning(LintCategory.SERIAL,
5542 TreeInfo.diagnosticPositionFor(method, tree),
5543 Warnings.IneffectualSerialMethodRecord(name));
5544 }
5545 }}
5546 }}});
5547 }
5548 return null;
5549 }
5550
5551 void checkConcreteInstanceMethod(JCClassDecl tree,
5552 Element enclosing,
5553 MethodSymbol method) {
5554 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5555 log.warning(LintCategory.SERIAL,
5556 TreeInfo.diagnosticPositionFor(method, tree),
5557 Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5558 }
5559 }
5560
5561 private void checkReturnType(JCClassDecl tree,
5562 Element enclosing,
5563 MethodSymbol method,
5564 Type expectedReturnType) {
5565 // Note: there may be complications checking writeReplace
5566 // and readResolve since they return Object and could, in
5567 // principle, have covariant overrides and any synthetic
5568 // bridge method would not be represented here for
5569 // checking.
5570 Type rtype = method.getReturnType();
5571 if (!types.isSameType(expectedReturnType, rtype)) {
5572 log.warning(LintCategory.SERIAL,
5573 TreeInfo.diagnosticPositionFor(method, tree),
5574 Warnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5575 rtype, expectedReturnType));
5576 }
5577 }
5578
5579 private void checkOneArg(JCClassDecl tree,
5580 Element enclosing,
5581 MethodSymbol method,
5582 Type expectedType) {
5583 String name = method.getSimpleName().toString();
5584
5585 var parameters= method.getParameters();
5586
5587 if (parameters.size() != 1) {
5588 log.warning(LintCategory.SERIAL,
5589 TreeInfo.diagnosticPositionFor(method, tree),
5590 Warnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5591 return;
5592 }
5593
5594 Type parameterType = parameters.get(0).asType();
5595 if (!types.isSameType(parameterType, expectedType)) {
5596 log.warning(LintCategory.SERIAL,
5597 TreeInfo.diagnosticPositionFor(method, tree),
5598 Warnings.SerialMethodParameterType(method.getSimpleName(),
5599 expectedType,
5600 parameterType));
5601 }
5602 }
5603
5604 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5605 Element enclosing,
5606 MethodSymbol method,
5607 Type expectedType) {
5608 var parameters = method.getParameters();
5609 return (parameters.size() == 1) &&
5610 types.isSameType(parameters.get(0).asType(), expectedType);
5611 }
5612
5613
5614 private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5615 var parameters = method.getParameters();
5616 if (!parameters.isEmpty()) {
5617 log.warning(LintCategory.SERIAL,
5618 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5619 Warnings.SerialMethodNoArgs(method.getSimpleName()));
5620 }
5621 }
5622
5623 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5624 // If the enclosing class is externalizable, warn for the method
5625 if (isExternalizable((Type)enclosing.asType())) {
5626 log.warning(LintCategory.SERIAL,
5627 TreeInfo.diagnosticPositionFor(method, tree),
5628 Warnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5629 }
5630 return;
5631 }
5632
5633 private void checkExceptions(JCClassDecl tree,
5634 Element enclosing,
5635 MethodSymbol method,
5636 Type... declaredExceptions) {
5637 for (Type thrownType: method.getThrownTypes()) {
5638 // For each exception in the throws clause of the
5639 // method, if not an Error and not a RuntimeException,
5640 // check if the exception is a subtype of a declared
5641 // exception from the throws clause of the
5642 // serialization method in question.
5643 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5644 types.isSubtype(thrownType, syms.errorType) ) {
5645 continue;
5646 } else {
5647 boolean declared = false;
5648 for (Type declaredException : declaredExceptions) {
5649 if (types.isSubtype(thrownType, declaredException)) {
5650 declared = true;
5651 continue;
5652 }
5653 }
5654 if (!declared) {
5655 log.warning(LintCategory.SERIAL,
5656 TreeInfo.diagnosticPositionFor(method, tree),
5657 Warnings.SerialMethodUnexpectedException(method.getSimpleName(),
5658 thrownType));
5659 }
5660 }
5661 }
5662 return;
5663 }
5664
5665 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5666 Lint prevLint = lint;
5667 try {
5668 lint = lint.augment((Symbol) symbol);
5669
5670 if (lint.isEnabled(LintCategory.SERIAL)) {
5671 task.accept(symbol, p);
5672 }
5673
5674 return null;
5675 } finally {
5676 lint = prevLint;
5677 }
5678 }
5679
5680 }
5681
5682 }
|
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 : ExtendedVarFlags;
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 // Interfaces are always ABSTRACT
1334 if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
1335
1336 if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
1337 implicit |= IDENTITY_TYPE;
1338 }
1339
1340 if ((flags & ENUM) != 0) {
1341 // enums can't be declared abstract, final, sealed or non-sealed or value
1342 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
1343 implicit |= implicitEnumFinalFlag(tree);
1344 }
1345 if ((flags & RECORD) != 0) {
1346 // records can't be declared abstract
1347 mask &= ~ABSTRACT;
1348 implicit |= FINAL;
1349 }
1350 if ((flags & STRICTFP) != 0) {
1351 warnOnExplicitStrictfp(pos);
1352 }
1353 // Imply STRICTFP if owner has STRICTFP set.
1354 implicit |= sym.owner.flags_field & STRICTFP;
1355
1356 // concrete value classes are implicitly final
1357 if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
1358 implicit |= FINAL;
1359 }
1360 break;
1361 default:
1362 throw new AssertionError();
1363 }
1364 long illegal = flags & ExtendedStandardFlags & ~mask;
1365 if (illegal != 0) {
1366 if ((illegal & INTERFACE) != 0) {
1367 log.error(pos, ((flags & ANNOTATION) != 0) ? Errors.AnnotationDeclNotAllowedHere : Errors.IntfNotAllowedHere);
1368 mask |= INTERFACE;
1369 }
1370 else {
1371 log.error(pos,
1372 Errors.ModNotAllowedHere(asFlagSet(illegal)));
1373 }
1374 }
1375 else if ((sym.kind == TYP ||
1376 // ISSUE: Disallowing abstract&private is no longer appropriate
1377 // in the presence of inner classes. Should it be deleted here?
1378 checkDisjoint(pos, flags,
1379 ABSTRACT,
1394 checkDisjoint(pos, flags,
1395 PRIVATE,
1396 PUBLIC | PROTECTED)
1397 &&
1398 checkDisjoint(pos, flags,
1399 FINAL,
1400 VOLATILE)
1401 &&
1402 (sym.kind == TYP ||
1403 checkDisjoint(pos, flags,
1404 ABSTRACT | NATIVE,
1405 STRICTFP))
1406 && checkDisjoint(pos, flags,
1407 FINAL,
1408 SEALED | NON_SEALED)
1409 && checkDisjoint(pos, flags,
1410 SEALED,
1411 FINAL | NON_SEALED)
1412 && checkDisjoint(pos, flags,
1413 SEALED,
1414 ANNOTATION)
1415 && checkDisjoint(pos, flags,
1416 VALUE_CLASS,
1417 ANNOTATION)
1418 && checkDisjoint(pos, flags,
1419 VALUE_CLASS,
1420 NON_SEALED)
1421 && checkDisjoint(pos, flags,
1422 VALUE_CLASS,
1423 INTERFACE) ) {
1424 // skip
1425 }
1426 return flags & (mask | ~ExtendedStandardFlags) | implicit;
1427 }
1428
1429 private void warnOnExplicitStrictfp(DiagnosticPosition pos) {
1430 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(pos);
1431 try {
1432 deferredLintHandler.report(() -> {
1433 if (lint.isEnabled(LintCategory.STRICTFP)) {
1434 log.warning(LintCategory.STRICTFP,
1435 pos, Warnings.Strictfp); }
1436 });
1437 } finally {
1438 deferredLintHandler.setPos(prevLintPos);
1439 }
1440 }
1441
1442
1443 /** Determine if this enum should be implicitly final.
2220 return true;
2221 }
2222 }
2223 }
2224 return false;
2225 }
2226
2227 /** Check that a given method conforms with any method it overrides.
2228 * @param tree The tree from which positions are extracted
2229 * for errors.
2230 * @param m The overriding method.
2231 */
2232 void checkOverride(Env<AttrContext> env, JCMethodDecl tree, MethodSymbol m) {
2233 ClassSymbol origin = (ClassSymbol)m.owner;
2234 if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) {
2235 if (m.overrides(syms.enumFinalFinalize, origin, types, false)) {
2236 log.error(tree.pos(), Errors.EnumNoFinalize);
2237 return;
2238 }
2239 }
2240 if (allowValueClasses && origin.isValueClass() && names.finalize.equals(m.name)) {
2241 if (m.overrides(syms.objectFinalize, origin, types, false)) {
2242 log.warning(tree.pos(), Warnings.ValueFinalize);
2243 }
2244 }
2245 if (allowRecords && origin.isRecord()) {
2246 // let's find out if this is a user defined accessor in which case the @Override annotation is acceptable
2247 Optional<? extends RecordComponent> recordComponent = origin.getRecordComponents().stream()
2248 .filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
2249 if (recordComponent.isPresent()) {
2250 return;
2251 }
2252 }
2253
2254 for (Type t = origin.type; t.hasTag(CLASS);
2255 t = types.supertype(t)) {
2256 if (t != origin.type) {
2257 checkOverride(tree, t, origin, m);
2258 }
2259 for (Type t2 : types.interfaces(t)) {
2260 checkOverride(tree, t2, origin, m);
2261 }
2262 }
2263
2264 final boolean explicitOverride = m.attribute(syms.overrideType.tsym) != null;
2653 /** Check that all abstract methods implemented by a class are
2654 * mutually compatible.
2655 * @param pos Position to be used for error reporting.
2656 * @param c The class whose interfaces are checked.
2657 */
2658 void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) {
2659 List<Type> supertypes = types.interfaces(c);
2660 Type supertype = types.supertype(c);
2661 if (supertype.hasTag(CLASS) &&
2662 (supertype.tsym.flags() & ABSTRACT) != 0)
2663 supertypes = supertypes.prepend(supertype);
2664 for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) {
2665 if (!l.head.getTypeArguments().isEmpty() &&
2666 !checkCompatibleAbstracts(pos, l.head, l.head, c))
2667 return;
2668 for (List<Type> m = supertypes; m != l; m = m.tail)
2669 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
2670 return;
2671 }
2672 checkCompatibleConcretes(pos, c);
2673
2674 Type identitySuper = null;
2675 for (Type t : types.closure(c)) {
2676 if (t != c) {
2677 if (t.isIdentityClass() && (t.tsym.flags() & VALUE_BASED) == 0)
2678 identitySuper = t;
2679 if (c.isValueClass() && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
2680 log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
2681 break;
2682 }
2683 }
2684 }
2685 }
2686
2687 /** Check that all non-override equivalent methods accessible from 'site'
2688 * are mutually compatible (JLS 8.4.8/9.4.1).
2689 *
2690 * @param pos Position to be used for error reporting.
2691 * @param site The class whose methods are checked.
2692 * @param sym The method symbol to be checked.
2693 */
2694 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
2695 ClashFilter cf = new ClashFilter(site);
2696 //for each method m1 that is overridden (directly or indirectly)
2697 //by method 'sym' in 'site'...
2698
2699 ArrayList<Symbol> symbolsByName = new ArrayList<>();
2700 types.membersClosure(site, false).getSymbolsByName(sym.name, cf).forEach(symbolsByName::add);
2701 for (Symbol m1 : symbolsByName) {
2702 if (!sym.overrides(m1, site.tsym, types, false)) {
2703 continue;
2704 }
4139 break;
4140
4141 // super()/this() calls must only appear in a constructor
4142 if (!constructor) {
4143 log.error(apply.pos(), Errors.CallMustOnlyAppearInCtor);
4144 break;
4145 }
4146
4147 // super()/this() calls must be a top level statement
4148 if (scanDepth != MATCH_SCAN_DEPTH) {
4149 log.error(apply.pos(), Errors.CtorCallsNotAllowedHere);
4150 break;
4151 }
4152
4153 // super()/this() calls must not appear more than once
4154 if (initCall != null) {
4155 log.error(apply.pos(), Errors.RedundantSuperclassInit);
4156 break;
4157 }
4158
4159 // If super()/this() isn't first, require flexible constructors feature
4160 if (!firstStatement)
4161 preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT);
4162
4163 // We found a legitimate super()/this() call; remember it
4164 initCall = methodName;
4165 } while (false);
4166
4167 // Proceed
4168 super.visitApply(apply);
4169 }
4170
4171 @Override
4172 public void visitReturn(JCReturn tree) {
4173 if (constructor && initCall == null && earlyReturn == null)
4174 earlyReturn = tree; // we have seen a return but not (yet) a super()/this()
4175 super.visitReturn(tree);
4176 }
4177
4178 @Override
4179 public void visitClassDef(JCClassDecl tree) {
5000 }
5001 } else {
5002 Assert.error("Unknown pattern: " + currentPattern.getTag());
5003 }
5004 return false;
5005 }
5006
5007 /** check if a type is a subtype of Externalizable, if that is available. */
5008 boolean isExternalizable(Type t) {
5009 try {
5010 syms.externalizableType.complete();
5011 } catch (CompletionFailure e) {
5012 return false;
5013 }
5014 return types.isSubtype(t, syms.externalizableType);
5015 }
5016
5017 /**
5018 * Check structure of serialization declarations.
5019 */
5020 public void checkSerialStructure(Env<AttrContext> env, JCClassDecl tree, ClassSymbol c) {
5021 (new SerialTypeVisitor(env)).visit(c, tree);
5022 }
5023
5024 /**
5025 * This visitor will warn if a serialization-related field or
5026 * method is declared in a suspicious or incorrect way. In
5027 * particular, it will warn for cases where the runtime
5028 * serialization mechanism will silently ignore a mis-declared
5029 * entity.
5030 *
5031 * Distinguished serialization-related fields and methods:
5032 *
5033 * Methods:
5034 *
5035 * private void writeObject(ObjectOutputStream stream) throws IOException
5036 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
5037 *
5038 * private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
5039 * private void readObjectNoData() throws ObjectStreamException
5040 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
5041 *
5042 * Fields:
5043 *
5044 * private static final long serialVersionUID
5045 * private static final ObjectStreamField[] serialPersistentFields
5046 *
5047 * Externalizable: methods defined on the interface
5048 * public void writeExternal(ObjectOutput) throws IOException
5049 * public void readExternal(ObjectInput) throws IOException
5050 */
5051 private class SerialTypeVisitor extends ElementKindVisitor14<Void, JCClassDecl> {
5052 Env<AttrContext> env;
5053 SerialTypeVisitor(Env<AttrContext> env) {
5054 this.lint = Check.this.lint;
5055 this.env = env;
5056 }
5057
5058 private static final Set<String> serialMethodNames =
5059 Set.of("writeObject", "writeReplace",
5060 "readObject", "readObjectNoData",
5061 "readResolve");
5062
5063 private static final Set<String> serialFieldNames =
5064 Set.of("serialVersionUID", "serialPersistentFields");
5065
5066 // Type of serialPersistentFields
5067 private final Type OSF_TYPE = new Type.ArrayType(syms.objectStreamFieldType, syms.arrayClass);
5068
5069 Lint lint;
5070
5071 @Override
5072 public Void defaultAction(Element e, JCClassDecl p) {
5073 throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), ""));
5074 }
5075
5095 if (sym.kind == VAR) {
5096 svuidSym = (VarSymbol)sym;
5097 break;
5098 }
5099 }
5100
5101 if (svuidSym == null) {
5102 log.warning(LintCategory.SERIAL, p.pos(), Warnings.MissingSVUID(c));
5103 }
5104
5105 // Check for serialPersistentFields to gate checks for
5106 // non-serializable non-transient instance fields
5107 boolean serialPersistentFieldsPresent =
5108 c.members()
5109 .getSymbolsByName(names.serialPersistentFields, sym -> sym.kind == VAR)
5110 .iterator()
5111 .hasNext();
5112
5113 // Check declarations of serialization-related methods and
5114 // fields
5115 final boolean[] hasWriteReplace = {false};
5116 for(Symbol el : c.getEnclosedElements()) {
5117 runUnderLint(el, p, (enclosed, tree) -> {
5118 String name = null;
5119 switch(enclosed.getKind()) {
5120 case FIELD -> {
5121 if (!serialPersistentFieldsPresent) {
5122 var flags = enclosed.flags();
5123 if ( ((flags & TRANSIENT) == 0) &&
5124 ((flags & STATIC) == 0)) {
5125 Type varType = enclosed.asType();
5126 if (!canBeSerialized(varType)) {
5127 // Note per JLS arrays are
5128 // serializable even if the
5129 // component type is not.
5130 log.warning(LintCategory.SERIAL,
5131 TreeInfo.diagnosticPositionFor(enclosed, tree),
5132 Warnings.NonSerializableInstanceField);
5133 } else if (varType.hasTag(ARRAY)) {
5134 ArrayType arrayType = (ArrayType)varType;
5135 Type elementType = arrayType.elemtype;
5170 // will also pull in default methods from
5171 // superinterfaces. In other words, the runtime checks
5172 // (which long predate default methods on interfaces)
5173 // do not admit the possibility of inheriting methods
5174 // this way, a difference from general inheritance.
5175
5176 // The current implementation just checks the enclosed
5177 // elements and does not directly check the inherited
5178 // methods. If all the types are being checked this is
5179 // less of a concern; however, there are cases that
5180 // could be missed. In particular, readResolve and
5181 // writeReplace could, in principle, by inherited from
5182 // a non-serializable superclass and thus not checked
5183 // even if compiled with a serializable child class.
5184 case METHOD -> {
5185 var method = (MethodSymbol)enclosed;
5186 name = method.getSimpleName().toString();
5187 if (serialMethodNames.contains(name)) {
5188 switch (name) {
5189 case "writeObject" -> checkWriteObject(tree, e, method);
5190 case "writeReplace" -> {hasWriteReplace[0] = true; hasAppropriateWriteReplace(tree, method, true);}
5191 case "readObject" -> checkReadObject(tree,e, method);
5192 case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
5193 case "readResolve" -> checkReadResolve(tree, e, method);
5194 default -> throw new AssertionError();
5195 }
5196 }
5197 }
5198 }
5199 });
5200 }
5201 if (!hasWriteReplace[0] &&
5202 (c.isValueClass() || hasAbstractValueSuperClass(c, Set.of(syms.numberType.tsym))) &&
5203 !c.isAbstract() && !c.isRecord() &&
5204 types.unboxedType(c.type) == Type.noType) {
5205 // we need to check if the class is inheriting an appropriate writeReplace method
5206 MethodSymbol ms = null;
5207 Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log);
5208 try {
5209 ms = rs.resolveInternalMethod(env.tree, env, c.type, names.writeReplace, List.nil(), List.nil());
5210 } catch (FatalError fe) {
5211 // ignore no method was found
5212 } finally {
5213 log.popDiagnosticHandler(discardHandler);
5214 }
5215 if (ms == null || !hasAppropriateWriteReplace(p, ms, false)) {
5216 log.warning(LintCategory.SERIAL, p,
5217 c.isValueClass() ? Warnings.SerializableValueClassWithoutWriteReplace1 :
5218 Warnings.SerializableValueClassWithoutWriteReplace2);
5219 }
5220 }
5221 return null;
5222 }
5223
5224 boolean canBeSerialized(Type type) {
5225 return type.isPrimitive() || rs.isSerializable(type);
5226 }
5227
5228 private boolean hasAbstractValueSuperClass(Symbol c, Set<Symbol> excluding) {
5229 while (c.getKind() == ElementKind.CLASS) {
5230 Type sup = ((ClassSymbol)c).getSuperclass();
5231 if (!sup.hasTag(CLASS) || sup.isErroneous() ||
5232 sup.tsym == syms.objectType.tsym) {
5233 return false;
5234 }
5235 // if it is a value super class it has to be abstract
5236 if (sup.isValueClass() && !excluding.contains(sup.tsym)) {
5237 return true;
5238 }
5239 c = sup.tsym;
5240 }
5241 return false;
5242 }
5243
5244 /**
5245 * Check that Externalizable class needs a public no-arg
5246 * constructor.
5247 *
5248 * Check that a Serializable class has access to the no-arg
5249 * constructor of its first nonserializable superclass.
5250 */
5251 private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) {
5252 if (isExternalizable(c.type)) {
5253 for(var sym : c.getEnclosedElements()) {
5254 if (sym.isConstructor() &&
5255 ((sym.flags() & PUBLIC) == PUBLIC)) {
5256 if (((MethodSymbol)sym).getParameters().isEmpty()) {
5257 return;
5258 }
5259 }
5260 }
5261 log.warning(LintCategory.SERIAL, tree.pos(),
5262 Warnings.ExternalizableMissingPublicNoArgCtor);
5263 } else {
5347 // Warn if serialPersistentFields is initialized to a
5348 // literal null.
5349 JCTree spfDecl = TreeInfo.declarationFor(spf, tree);
5350 if (spfDecl != null && spfDecl.getTag() == VARDEF) {
5351 JCVariableDecl variableDef = (JCVariableDecl) spfDecl;
5352 JCExpression initExpr = variableDef.init;
5353 if (initExpr != null && TreeInfo.isNull(initExpr)) {
5354 log.warning(LintCategory.SERIAL, initExpr.pos(),
5355 Warnings.SPFNullInit);
5356 }
5357 }
5358 }
5359
5360 private void checkWriteObject(JCClassDecl tree, Element e, MethodSymbol method) {
5361 // The "synchronized" modifier is seen in the wild on
5362 // readObject and writeObject methods and is generally
5363 // innocuous.
5364
5365 // private void writeObject(ObjectOutputStream stream) throws IOException
5366 checkPrivateNonStaticMethod(tree, method);
5367 isExpectedReturnType(tree, method, syms.voidType, true);
5368 checkOneArg(tree, e, method, syms.objectOutputStreamType);
5369 hasExpectedExceptions(tree, method, true, syms.ioExceptionType);
5370 checkExternalizable(tree, e, method);
5371 }
5372
5373 private boolean hasAppropriateWriteReplace(JCClassDecl tree, MethodSymbol method, boolean warn) {
5374 // ANY-ACCESS-MODIFIER Object writeReplace() throws
5375 // ObjectStreamException
5376
5377 // Excluding abstract, could have a more complicated
5378 // rule based on abstract-ness of the class
5379 return isConcreteInstanceMethod(tree, method, warn) &&
5380 isExpectedReturnType(tree, method, syms.objectType, warn) &&
5381 hasNoArgs(tree, method, warn) &&
5382 hasExpectedExceptions(tree, method, warn, syms.objectStreamExceptionType);
5383 }
5384
5385 private void checkReadObject(JCClassDecl tree, Element e, MethodSymbol method) {
5386 // The "synchronized" modifier is seen in the wild on
5387 // readObject and writeObject methods and is generally
5388 // innocuous.
5389
5390 // private void readObject(ObjectInputStream stream)
5391 // throws IOException, ClassNotFoundException
5392 checkPrivateNonStaticMethod(tree, method);
5393 isExpectedReturnType(tree, method, syms.voidType, true);
5394 checkOneArg(tree, e, method, syms.objectInputStreamType);
5395 hasExpectedExceptions(tree, method, true, syms.ioExceptionType, syms.classNotFoundExceptionType);
5396 checkExternalizable(tree, e, method);
5397 }
5398
5399 private void checkReadObjectNoData(JCClassDecl tree, Element e, MethodSymbol method) {
5400 // private void readObjectNoData() throws ObjectStreamException
5401 checkPrivateNonStaticMethod(tree, method);
5402 isExpectedReturnType(tree, method, syms.voidType, true);
5403 hasNoArgs(tree, method, true);
5404 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5405 checkExternalizable(tree, e, method);
5406 }
5407
5408 private void checkReadResolve(JCClassDecl tree, Element e, MethodSymbol method) {
5409 // ANY-ACCESS-MODIFIER Object readResolve()
5410 // throws ObjectStreamException
5411
5412 // Excluding abstract, could have a more complicated
5413 // rule based on abstract-ness of the class
5414 isConcreteInstanceMethod(tree, method, true);
5415 isExpectedReturnType(tree, method, syms.objectType, true);
5416 hasNoArgs(tree, method, true);
5417 hasExpectedExceptions(tree, method, true, syms.objectStreamExceptionType);
5418 }
5419
5420 private void checkWriteExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5421 //public void writeExternal(ObjectOutput) throws IOException
5422 checkExternMethodRecord(tree, e, method, syms.objectOutputType, isExtern);
5423 }
5424
5425 private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol method, boolean isExtern) {
5426 // public void readExternal(ObjectInput) throws IOException
5427 checkExternMethodRecord(tree, e, method, syms.objectInputType, isExtern);
5428 }
5429
5430 private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType,
5431 boolean isExtern) {
5432 if (isExtern && isExternMethod(tree, e, method, argType)) {
5433 log.warning(LintCategory.SERIAL,
5434 TreeInfo.diagnosticPositionFor(method, tree),
5435 Warnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString()));
5436 }
5437 }
5647 case FIELD -> {
5648 var field = (VarSymbol)enclosed;
5649 switch(name) {
5650 case "serialPersistentFields" -> {
5651 log.warning(LintCategory.SERIAL,
5652 TreeInfo.diagnosticPositionFor(field, tree),
5653 Warnings.IneffectualSerialFieldRecord);
5654 }
5655
5656 case "serialVersionUID" -> {
5657 // Could generate additional warning that
5658 // svuid value is not checked to match for
5659 // records.
5660 checkSerialVersionUID(tree, e, field);
5661 }}
5662 }
5663
5664 case METHOD -> {
5665 var method = (MethodSymbol)enclosed;
5666 switch(name) {
5667 case "writeReplace" -> hasAppropriateWriteReplace(tree, method, true);
5668 case "readResolve" -> checkReadResolve(tree, e, method);
5669
5670 case "writeExternal" -> checkWriteExternalRecord(tree, e, method, isExtern);
5671 case "readExternal" -> checkReadExternalRecord(tree, e, method, isExtern);
5672
5673 default -> {
5674 if (serialMethodNames.contains(name)) {
5675 log.warning(LintCategory.SERIAL,
5676 TreeInfo.diagnosticPositionFor(method, tree),
5677 Warnings.IneffectualSerialMethodRecord(name));
5678 }
5679 }}
5680 }}});
5681 }
5682 return null;
5683 }
5684
5685 boolean isConcreteInstanceMethod(JCClassDecl tree,
5686 MethodSymbol method,
5687 boolean warn) {
5688 if ((method.flags() & (STATIC | ABSTRACT)) != 0) {
5689 if (warn) {
5690 log.warning(LintCategory.SERIAL,
5691 TreeInfo.diagnosticPositionFor(method, tree),
5692 Warnings.SerialConcreteInstanceMethod(method.getSimpleName()));
5693 }
5694 return false;
5695 }
5696 return true;
5697 }
5698
5699 private boolean isExpectedReturnType(JCClassDecl tree,
5700 MethodSymbol method,
5701 Type expectedReturnType,
5702 boolean warn) {
5703 // Note: there may be complications checking writeReplace
5704 // and readResolve since they return Object and could, in
5705 // principle, have covariant overrides and any synthetic
5706 // bridge method would not be represented here for
5707 // checking.
5708 Type rtype = method.getReturnType();
5709 if (!types.isSameType(expectedReturnType, rtype)) {
5710 if (warn) {
5711 log.warning(LintCategory.SERIAL,
5712 TreeInfo.diagnosticPositionFor(method, tree),
5713 Warnings.SerialMethodUnexpectedReturnType(method.getSimpleName(),
5714 rtype, expectedReturnType));
5715 }
5716 return false;
5717 }
5718 return true;
5719 }
5720
5721 private void checkOneArg(JCClassDecl tree,
5722 Element enclosing,
5723 MethodSymbol method,
5724 Type expectedType) {
5725 String name = method.getSimpleName().toString();
5726
5727 var parameters= method.getParameters();
5728
5729 if (parameters.size() != 1) {
5730 log.warning(LintCategory.SERIAL,
5731 TreeInfo.diagnosticPositionFor(method, tree),
5732 Warnings.SerialMethodOneArg(method.getSimpleName(), parameters.size()));
5733 return;
5734 }
5735
5736 Type parameterType = parameters.get(0).asType();
5737 if (!types.isSameType(parameterType, expectedType)) {
5738 log.warning(LintCategory.SERIAL,
5739 TreeInfo.diagnosticPositionFor(method, tree),
5740 Warnings.SerialMethodParameterType(method.getSimpleName(),
5741 expectedType,
5742 parameterType));
5743 }
5744 }
5745
5746 private boolean hasExactlyOneArgWithType(JCClassDecl tree,
5747 Element enclosing,
5748 MethodSymbol method,
5749 Type expectedType) {
5750 var parameters = method.getParameters();
5751 return (parameters.size() == 1) &&
5752 types.isSameType(parameters.get(0).asType(), expectedType);
5753 }
5754
5755
5756 boolean hasNoArgs(JCClassDecl tree, MethodSymbol method, boolean warn) {
5757 var parameters = method.getParameters();
5758 if (!parameters.isEmpty()) {
5759 if (warn) {
5760 log.warning(LintCategory.SERIAL,
5761 TreeInfo.diagnosticPositionFor(parameters.get(0), tree),
5762 Warnings.SerialMethodNoArgs(method.getSimpleName()));
5763 }
5764 return false;
5765 }
5766 return true;
5767 }
5768
5769 private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) {
5770 // If the enclosing class is externalizable, warn for the method
5771 if (isExternalizable((Type)enclosing.asType())) {
5772 log.warning(LintCategory.SERIAL,
5773 TreeInfo.diagnosticPositionFor(method, tree),
5774 Warnings.IneffectualSerialMethodExternalizable(method.getSimpleName()));
5775 }
5776 return;
5777 }
5778
5779 private boolean hasExpectedExceptions(JCClassDecl tree,
5780 MethodSymbol method,
5781 boolean warn,
5782 Type... declaredExceptions) {
5783 for (Type thrownType: method.getThrownTypes()) {
5784 // For each exception in the throws clause of the
5785 // method, if not an Error and not a RuntimeException,
5786 // check if the exception is a subtype of a declared
5787 // exception from the throws clause of the
5788 // serialization method in question.
5789 if (types.isSubtype(thrownType, syms.runtimeExceptionType) ||
5790 types.isSubtype(thrownType, syms.errorType) ) {
5791 continue;
5792 } else {
5793 boolean declared = false;
5794 for (Type declaredException : declaredExceptions) {
5795 if (types.isSubtype(thrownType, declaredException)) {
5796 declared = true;
5797 continue;
5798 }
5799 }
5800 if (!declared) {
5801 if (warn) {
5802 log.warning(LintCategory.SERIAL,
5803 TreeInfo.diagnosticPositionFor(method, tree),
5804 Warnings.SerialMethodUnexpectedException(method.getSimpleName(),
5805 thrownType));
5806 }
5807 return false;
5808 }
5809 }
5810 }
5811 return true;
5812 }
5813
5814 private <E extends Element> Void runUnderLint(E symbol, JCClassDecl p, BiConsumer<E, JCClassDecl> task) {
5815 Lint prevLint = lint;
5816 try {
5817 lint = lint.augment((Symbol) symbol);
5818
5819 if (lint.isEnabled(LintCategory.SERIAL)) {
5820 task.accept(symbol, p);
5821 }
5822
5823 return null;
5824 } finally {
5825 lint = prevLint;
5826 }
5827 }
5828
5829 }
5830
5831 }
|