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