1 /******************************************************************************* 2 * Copyright (c) 2000, 2020 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Stephan Herrmann - Contributions for 14 * bug 349326 - [1.7] new warning for missing try-with-resources 15 * bug 186342 - [compiler][null] Using annotations for null checking 16 * bug 365519 - editorial cleanup after bug 186342 and bug 365387 17 * bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK 18 * bug 382353 - [1.8][compiler] Implementation property modifiers should be accepted on default methods. 19 * bug 383368 - [compiler][null] syntactic null analysis for field references 20 * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis 21 * Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations 22 * Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables 23 * Bug 438012 - [1.8][null] Bogus Warning: The nullness annotation is redundant with a default that applies to this location 24 * Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations 25 * Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param 26 * Jesper S Moller <jesper@selskabet.org> - Contributions for 27 * bug 378674 - "The method can be declared as static" is wrong 28 *******************************************************************************/ 29 package org.eclipse.jdt.internal.compiler.ast; 30 31 import java.util.List; 32 33 import org.eclipse.jdt.core.compiler.CharOperation; 34 import org.eclipse.jdt.internal.compiler.ASTVisitor; 35 import org.eclipse.jdt.internal.compiler.CompilationResult; 36 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 37 import org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext; 38 import org.eclipse.jdt.internal.compiler.flow.FlowContext; 39 import org.eclipse.jdt.internal.compiler.flow.FlowInfo; 40 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; 41 import org.eclipse.jdt.internal.compiler.lookup.BlockScope; 42 import org.eclipse.jdt.internal.compiler.lookup.ClassScope; 43 import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; 44 import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; 45 import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding; 46 import org.eclipse.jdt.internal.compiler.lookup.TagBits; 47 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; 48 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; 49 import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; 50 import org.eclipse.jdt.internal.compiler.parser.Parser; 51 import org.eclipse.jdt.internal.compiler.problem.AbortMethod; 52 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; 53 import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationCollector; 54 import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition; 55 56 public class MethodDeclaration extends AbstractMethodDeclaration { 57 58 public TypeReference returnType; 59 public TypeParameter[] typeParameters; 60 61 /** 62 * MethodDeclaration constructor comment. 63 */ MethodDeclaration(CompilationResult compilationResult)64 public MethodDeclaration(CompilationResult compilationResult) { 65 super(compilationResult); 66 this.bits |= ASTNode.CanBeStatic; // Start with this assumption, will course correct during resolve and analyseCode. 67 } 68 analyseCode(ClassScope classScope, FlowContext flowContext, FlowInfo flowInfo)69 public void analyseCode(ClassScope classScope, FlowContext flowContext, FlowInfo flowInfo) { 70 // starting of the code analysis for methods 71 if (this.ignoreFurtherInvestigation) 72 return; 73 try { 74 if (this.binding == null) 75 return; 76 77 if (!this.binding.isUsed() && !this.binding.isAbstract()) { 78 if (this.binding.isPrivate() 79 || (((this.binding.modifiers & (ExtraCompilerModifiers.AccOverriding|ExtraCompilerModifiers.AccImplementing)) == 0) 80 && this.binding.isOrEnclosedByPrivateType())) { 81 if (!classScope.referenceCompilationUnit().compilationResult.hasSyntaxError) { 82 this.scope.problemReporter().unusedPrivateMethod(this); 83 } 84 } 85 } 86 87 // skip enum implicit methods 88 if (this.binding.declaringClass.isEnum() && (this.selector == TypeConstants.VALUES || this.selector == TypeConstants.VALUEOF)) 89 return; 90 91 // may be in a non necessary <clinit> for innerclass with static final constant fields 92 if (this.binding.isAbstract() || this.binding.isNative()) 93 return; 94 95 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780 96 if (this.typeParameters != null && 97 !this.scope.referenceCompilationUnit().compilationResult.hasSyntaxError) { 98 for (int i = 0, length = this.typeParameters.length; i < length; ++i) { 99 TypeParameter typeParameter = this.typeParameters[i]; 100 if ((typeParameter.binding.modifiers & ExtraCompilerModifiers.AccLocallyUsed) == 0) { 101 this.scope.problemReporter().unusedTypeParameter(typeParameter); 102 } 103 } 104 } 105 ExceptionHandlingFlowContext methodContext = 106 new ExceptionHandlingFlowContext( 107 flowContext, 108 this, 109 this.binding.thrownExceptions, 110 null, 111 this.scope, 112 FlowInfo.DEAD_END); 113 114 // nullity and mark as assigned 115 analyseArguments(classScope.environment(), flowInfo, this.arguments, this.binding); 116 117 if (this.binding.declaringClass instanceof MemberTypeBinding && !this.binding.declaringClass.isStatic()) { 118 // method of a non-static member type can't be static. 119 this.bits &= ~ASTNode.CanBeStatic; 120 } 121 // propagate to statements 122 if (this.statements != null) { 123 CompilerOptions compilerOptions = this.scope.compilerOptions(); 124 boolean enableSyntacticNullAnalysisForFields = compilerOptions.enableSyntacticNullAnalysisForFields; 125 int complaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) == 0 ? Statement.NOT_COMPLAINED : Statement.COMPLAINED_FAKE_REACHABLE; 126 for (int i = 0, count = this.statements.length; i < count; i++) { 127 Statement stat = this.statements[i]; 128 if ((complaintLevel = stat.complainIfUnreachable(flowInfo, this.scope, complaintLevel, true)) < Statement.COMPLAINED_UNREACHABLE) { 129 flowInfo = stat.analyseCode(this.scope, methodContext, flowInfo); 130 } 131 if (enableSyntacticNullAnalysisForFields) { 132 methodContext.expireNullCheckedFieldInfo(); 133 } 134 if (compilerOptions.analyseResourceLeaks) { 135 FakedTrackingVariable.cleanUpUnassigned(this.scope, stat, flowInfo); 136 } 137 } 138 } else { 139 // method with empty body should not be flagged as static. 140 this.bits &= ~ASTNode.CanBeStatic; 141 } 142 // check for missing returning path 143 TypeBinding returnTypeBinding = this.binding.returnType; 144 if ((returnTypeBinding == TypeBinding.VOID) || isAbstract()) { 145 if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) { 146 this.bits |= ASTNode.NeedFreeReturn; 147 } 148 } else { 149 if (flowInfo != FlowInfo.DEAD_END) { 150 this.scope.problemReporter().shouldReturn(returnTypeBinding, this); 151 } 152 } 153 // check unreachable catch blocks 154 methodContext.complainIfUnusedExceptionHandlers(this); 155 // check unused parameters 156 this.scope.checkUnusedParameters(this.binding); 157 // check if the method could have been static 158 if (!this.binding.isStatic() && (this.bits & ASTNode.CanBeStatic) != 0 && !this.isDefaultMethod()) { 159 if(!this.binding.isOverriding() && !this.binding.isImplementing()) { 160 if (this.binding.isPrivate() || this.binding.isFinal() || this.binding.declaringClass.isFinal()) { 161 this.scope.problemReporter().methodCanBeDeclaredStatic(this); 162 } else { 163 this.scope.problemReporter().methodCanBePotentiallyDeclaredStatic(this); 164 } 165 } 166 167 } 168 this.scope.checkUnclosedCloseables(flowInfo, null, null/*don't report against a specific location*/, null); 169 } catch (AbortMethod e) { 170 this.ignoreFurtherInvestigation = true; 171 } 172 } 173 174 @Override getAllAnnotationContexts(int targetType, List allAnnotationContexts)175 public void getAllAnnotationContexts(int targetType, List allAnnotationContexts) { 176 AnnotationCollector collector = new AnnotationCollector(this.returnType, targetType, allAnnotationContexts); 177 for (int i = 0, max = this.annotations.length; i < max; i++) { 178 Annotation annotation = this.annotations[i]; 179 annotation.traverse(collector, (BlockScope) null); 180 } 181 } 182 hasNullTypeAnnotation(AnnotationPosition position)183 public boolean hasNullTypeAnnotation(AnnotationPosition position) { 184 // parser associates SE8 annotations to the declaration 185 return TypeReference.containsNullAnnotation(this.annotations) || 186 (this.returnType != null && this.returnType.hasNullTypeAnnotation(position)); // just in case 187 } 188 189 @Override isDefaultMethod()190 public boolean isDefaultMethod() { 191 return (this.modifiers & ExtraCompilerModifiers.AccDefaultMethod) != 0; 192 } 193 194 @Override isMethod()195 public boolean isMethod() { 196 return true; 197 } 198 199 @Override getRecordComponent()200 public RecordComponent getRecordComponent() { 201 if (this.arguments != null && this.arguments.length != 0) 202 return null; 203 ClassScope skope = this.scope.classScope(); 204 TypeDeclaration typeDecl = skope.referenceContext; 205 if (!typeDecl.isRecord()) 206 return null; 207 if (!(skope.referenceContext.isRecord())) 208 return null; 209 RecordComponent[] recComps = typeDecl.recordComponents; 210 if (recComps == null || recComps.length == 0) 211 return null; 212 for (RecordComponent recComp : recComps) { 213 if (recComp == null || recComp.name == null) 214 continue; 215 if (CharOperation.equals(this.selector, recComp.name)) { 216 return recComp; 217 } 218 } 219 return null; 220 } 221 222 @Override parseStatements(Parser parser, CompilationUnitDeclaration unit)223 public void parseStatements(Parser parser, CompilationUnitDeclaration unit) { 224 //fill up the method body with statement 225 parser.parse(this, unit); 226 this.containsSwitchWithTry = parser.switchWithTry; 227 } 228 229 @Override printReturnType(int indent, StringBuffer output)230 public StringBuffer printReturnType(int indent, StringBuffer output) { 231 if (this.returnType == null) return output; 232 return this.returnType.printExpression(0, output).append(' '); 233 } 234 235 @Override resolveStatements()236 public void resolveStatements() { 237 // ========= abort on fatal error ============= 238 if (this.returnType != null && this.binding != null) { 239 this.bits |= (this.returnType.bits & ASTNode.HasTypeAnnotations); 240 this.returnType.resolvedType = this.binding.returnType; 241 // record the return type binding 242 } 243 RecordComponent recordComponent = getRecordComponent(); 244 if (recordComponent != null) { 245 /* JLS 14 Records Sec 8.10.3 */ 246 if (this.returnType != null && TypeBinding.notEquals(this.returnType.resolvedType, recordComponent.type.resolvedType)) 247 this.scope.problemReporter().recordIllegalAccessorReturnType(this.returnType, recordComponent.type.resolvedType); 248 if (this.typeParameters != null) 249 this.scope.problemReporter().recordAccessorMethodShouldNotBeGeneric(this); 250 if (this.binding != null) { 251 if ((this.binding.modifiers & ClassFileConstants.AccPublic) == 0) 252 this.scope.problemReporter().recordAccessorMethodShouldBePublic(this); 253 if ((this.binding.modifiers & ClassFileConstants.AccStatic) != 0) 254 this.scope.problemReporter().recordAccessorMethodShouldNotBeStatic(this); 255 } 256 if (this.thrownExceptions != null) 257 this.scope.problemReporter().recordAccessorMethodHasThrowsClause(this); 258 } 259 // check if method with constructor name 260 if (CharOperation.equals(this.scope.enclosingSourceType().sourceName, this.selector)) { 261 this.scope.problemReporter().methodWithConstructorName(this); 262 } 263 // to check whether the method returns a type parameter not declared by it. 264 boolean returnsUndeclTypeVar = false; 265 if (this.returnType != null && this.returnType.resolvedType instanceof TypeVariableBinding) { 266 returnsUndeclTypeVar = true; 267 } 268 if (this.typeParameters != null) { 269 for (int i = 0, length = this.typeParameters.length; i < length; i++) { 270 TypeParameter typeParameter = this.typeParameters[i]; 271 this.bits |= (typeParameter.bits & ASTNode.HasTypeAnnotations); 272 // typeParameter is already resolved from Scope#connectTypeVariables() 273 if (returnsUndeclTypeVar && TypeBinding.equalsEquals(this.typeParameters[i].binding, this.returnType.resolvedType)) { 274 returnsUndeclTypeVar = false; 275 } 276 } 277 } 278 279 // check @Override annotation 280 final CompilerOptions compilerOptions = this.scope.compilerOptions(); 281 checkOverride: { 282 if (this.binding == null) break checkOverride; 283 long complianceLevel = compilerOptions.complianceLevel; 284 if (complianceLevel < ClassFileConstants.JDK1_5) break checkOverride; 285 int bindingModifiers = this.binding.modifiers; 286 boolean hasOverrideAnnotation = (this.binding.tagBits & TagBits.AnnotationOverride) != 0; 287 boolean hasUnresolvedArguments = (this.binding.tagBits & TagBits.HasUnresolvedArguments) != 0; 288 if (hasOverrideAnnotation && !hasUnresolvedArguments) { 289 // no static method is considered overriding 290 if ((bindingModifiers & (ClassFileConstants.AccStatic|ExtraCompilerModifiers.AccOverriding)) == ExtraCompilerModifiers.AccOverriding) 291 break checkOverride; 292 // in 1.5, strictly for overriding superclass method 293 // in 1.6 and above, also tolerate implementing interface method 294 if (complianceLevel >= ClassFileConstants.JDK1_6 295 && ((bindingModifiers & (ClassFileConstants.AccStatic|ExtraCompilerModifiers.AccImplementing)) == ExtraCompilerModifiers.AccImplementing)) 296 break checkOverride; 297 // claims to override, and doesn't actually do so 298 this.scope.problemReporter().methodMustOverride(this, complianceLevel); 299 } else { 300 //In case of a concrete class method, we have to check if it overrides(in 1.5 and above) OR implements a method(1.6 and above). 301 //Also check if the method has a signature that is override-equivalent to that of any public method declared in Object. 302 if (!this.binding.declaringClass.isInterface()){ 303 if((bindingModifiers & (ClassFileConstants.AccStatic|ExtraCompilerModifiers.AccOverriding)) == ExtraCompilerModifiers.AccOverriding) { 304 this.scope.problemReporter().missingOverrideAnnotation(this); 305 } else { 306 if(complianceLevel >= ClassFileConstants.JDK1_6 307 && compilerOptions.reportMissingOverrideAnnotationForInterfaceMethodImplementation 308 && this.binding.isImplementing()) { 309 // actually overrides, but did not claim to do so 310 this.scope.problemReporter().missingOverrideAnnotationForInterfaceMethodImplementation(this); 311 } 312 313 } 314 } 315 else { //For 1.6 and above only 316 //In case of a interface class method, we have to check if it overrides a method (isImplementing returns true in case it overrides) 317 //Also check if the method has a signature that is override-equivalent to that of any public method declared in Object. 318 if(complianceLevel >= ClassFileConstants.JDK1_6 319 && compilerOptions.reportMissingOverrideAnnotationForInterfaceMethodImplementation 320 && (((bindingModifiers & (ClassFileConstants.AccStatic|ExtraCompilerModifiers.AccOverriding)) == ExtraCompilerModifiers.AccOverriding) || this.binding.isImplementing())){ 321 // actually overrides, but did not claim to do so 322 this.scope.problemReporter().missingOverrideAnnotationForInterfaceMethodImplementation(this); 323 } 324 } 325 } 326 } 327 328 switch (TypeDeclaration.kind(this.scope.referenceType().modifiers)) { 329 case TypeDeclaration.ENUM_DECL : 330 if (this.selector == TypeConstants.VALUES) break; 331 if (this.selector == TypeConstants.VALUEOF) break; 332 //$FALL-THROUGH$ 333 case TypeDeclaration.CLASS_DECL : 334 case TypeDeclaration.RECORD_DECL: 335 // if a method has an semicolon body and is not declared as abstract==>error 336 // native methods may have a semicolon body 337 if ((this.modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0) { 338 if ((this.modifiers & ClassFileConstants.AccNative) == 0) 339 if ((this.modifiers & ClassFileConstants.AccAbstract) == 0) 340 this.scope.problemReporter().methodNeedBody(this); 341 } else { 342 // the method HAS a body --> abstract native modifiers are forbidden 343 if (((this.modifiers & ClassFileConstants.AccNative) != 0) || ((this.modifiers & ClassFileConstants.AccAbstract) != 0)) 344 this.scope.problemReporter().methodNeedingNoBody(this); 345 else if (this.binding == null || this.binding.isStatic() || (this.binding.declaringClass instanceof LocalTypeBinding) || returnsUndeclTypeVar) { 346 // Cannot be static for one of the reasons stated above 347 this.bits &= ~ASTNode.CanBeStatic; 348 } 349 } 350 break; 351 case TypeDeclaration.INTERFACE_DECL : 352 if (compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8 353 && (this.modifiers & (ExtraCompilerModifiers.AccSemicolonBody | ClassFileConstants.AccAbstract)) == ExtraCompilerModifiers.AccSemicolonBody) { 354 boolean isPrivateMethod = compilerOptions.sourceLevel >= ClassFileConstants.JDK9 && (this.modifiers & ClassFileConstants.AccPrivate) != 0; 355 if (isPrivateMethod || ((this.modifiers & (ClassFileConstants.AccStatic | ExtraCompilerModifiers.AccDefaultMethod)) != 0)) { 356 this.scope.problemReporter().methodNeedBody(this); 357 } 358 } 359 break; 360 } 361 super.resolveStatements(); 362 363 // TagBits.OverridingMethodWithSupercall is set during the resolveStatements() call 364 if (compilerOptions.getSeverity(CompilerOptions.OverridingMethodWithoutSuperInvocation) != ProblemSeverities.Ignore) { 365 if (this.binding != null) { 366 int bindingModifiers = this.binding.modifiers; 367 if ((bindingModifiers & (ExtraCompilerModifiers.AccOverriding|ExtraCompilerModifiers.AccImplementing)) == ExtraCompilerModifiers.AccOverriding 368 && (this.bits & ASTNode.OverridingMethodWithSupercall) == 0) { 369 this.scope.problemReporter().overridesMethodWithoutSuperInvocation(this.binding); 370 } 371 } 372 } 373 } 374 375 @Override traverse( ASTVisitor visitor, ClassScope classScope)376 public void traverse( 377 ASTVisitor visitor, 378 ClassScope classScope) { 379 380 if (visitor.visit(this, classScope)) { 381 if (this.javadoc != null) { 382 this.javadoc.traverse(visitor, this.scope); 383 } 384 if (this.annotations != null) { 385 int annotationsLength = this.annotations.length; 386 for (int i = 0; i < annotationsLength; i++) 387 this.annotations[i].traverse(visitor, this.scope); 388 } 389 if (this.typeParameters != null) { 390 int typeParametersLength = this.typeParameters.length; 391 for (int i = 0; i < typeParametersLength; i++) { 392 this.typeParameters[i].traverse(visitor, this.scope); 393 } 394 } 395 if (this.returnType != null) 396 this.returnType.traverse(visitor, this.scope); 397 if (this.arguments != null) { 398 int argumentLength = this.arguments.length; 399 for (int i = 0; i < argumentLength; i++) 400 this.arguments[i].traverse(visitor, this.scope); 401 } 402 if (this.thrownExceptions != null) { 403 int thrownExceptionsLength = this.thrownExceptions.length; 404 for (int i = 0; i < thrownExceptionsLength; i++) 405 this.thrownExceptions[i].traverse(visitor, this.scope); 406 } 407 if (this.statements != null) { 408 int statementsLength = this.statements.length; 409 for (int i = 0; i < statementsLength; i++) 410 this.statements[i].traverse(visitor, this.scope); 411 } 412 } 413 visitor.endVisit(this, classScope); 414 } 415 @Override typeParameters()416 public TypeParameter[] typeParameters() { 417 return this.typeParameters; 418 } 419 } 420