org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 367203 - [compiler][null] detect assigning null to nonnull argument
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
* bug 382353 - [1.8][compiler] Implementation property modifiers should be accepted on default methods.
* bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 401030 - [1.8][null] Null analysis support for lambda methods.
* Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations
* Bug 403216 - [1.8][null] TypeReference#captureTypeAnnotations treats type annotations as type argument annotations
* Bug 417295 - [1.8[[null] Massage type annotated null analysis to gel well with deep encoded type bindings.
* Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations
* Bug 435570 - [1.8][null] @NonNullByDefault illegally tries to affect "throws E"
* Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
* Bug 466713 - Null Annotations: NullPointerException using as Type Param
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import java.util.List;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.util.Util;
@SuppressWarnings({"rawtypes"})
public abstract class AbstractMethodDeclaration
extends ASTNode
implements ProblemSeverities, ReferenceContext {
public MethodScope scope;
//it is not relevent for constructor but it helps to have the name of the constructor here
//which is always the name of the class.....parsing do extra work to fill it up while it do not have to....
public char[] selector;
public int declarationSourceStart;
public int declarationSourceEnd;
public int modifiers;
public int modifiersSourceStart;
public Annotation[] annotations;
// jsr 308
public Receiver receiver;
public Argument[] arguments;
public TypeReference[] thrownExceptions;
public Statement[] statements;
public int explicitDeclarations;
public MethodBinding binding;
public boolean ignoreFurtherInvestigation = false;
public Javadoc javadoc;
public int bodyStart;
public int bodyEnd = -1;
public CompilationResult compilationResult;
public boolean containsSwitchWithTry = false;
AbstractMethodDeclaration(CompilationResult compilationResult){
this.compilationResult = compilationResult;
this.containsSwitchWithTry = false;
}
/*
* We cause the compilation task to abort to a given extent.
*/
@Override
public void abort(int abortLevel, CategorizedProblem problem) {
switch (abortLevel) {
case AbortCompilation :
throw new AbortCompilation(this.compilationResult, problem);
case AbortCompilationUnit :
throw new AbortCompilationUnit(this.compilationResult, problem);
case AbortType :
throw new AbortType(this.compilationResult, problem);
default :
throw new AbortMethod(this.compilationResult, problem);
}
}
/**
* When a method is accessed via SourceTypeBinding.resolveTypesFor(MethodBinding)
* we create the argument binding and resolve annotations in order to compute null annotation tagbits.
*/
public void createArgumentBindings() {
createArgumentBindings(this.arguments, this.binding, this.scope);
}
// version for invocation from LambdaExpression:
static void createArgumentBindings(Argument[] arguments, MethodBinding binding, MethodScope scope) {
boolean useTypeAnnotations = scope.environment().usesNullTypeAnnotations();
if (arguments != null && binding != null) {
for (int i = 0, length = arguments.length; i < length; i++) {
Argument argument = arguments[i];
binding.parameters[i] = argument.createBinding(scope, binding.parameters[i]);
if (useTypeAnnotations)
continue; // no business with SE7 null annotations in the 1.8 case.
// createBinding() has resolved annotations, now transfer nullness info from the argument to the method:
long argTypeTagBits = (argument.binding.tagBits & TagBits.AnnotationNullMASK);
if (argTypeTagBits != 0) {
if (binding.parameterNonNullness == null) {
binding.parameterNonNullness = new Boolean[arguments.length];
binding.tagBits |= TagBits.IsNullnessKnown;
}
binding.parameterNonNullness[i] = Boolean.valueOf(argTypeTagBits == TagBits.AnnotationNonNull);
}
}
}
}
/**
* Bind and add argument's binding into the scope of the method
*/
public void bindArguments() {
if (this.arguments != null) {
// by default arguments in abstract/native methods are considered to be used (no complaint is expected)
if (this.binding == null) {
for (int i = 0, length = this.arguments.length; i < length; i++) {
this.arguments[i].bind(this.scope, null, true);
}
return;
}
boolean used = this.binding.isAbstract() || this.binding.isNative();
AnnotationBinding[][] paramAnnotations = null;
for (int i = 0, length = this.arguments.length; i < length; i++) {
Argument argument = this.arguments[i];
this.binding.parameters[i] = argument.bind(this.scope, this.binding.parameters[i], used);
if (argument.annotations != null) {
if (paramAnnotations == null) {
paramAnnotations = new AnnotationBinding[length][];
for (int j=0; j 0xFF) {
this.scope.problemReporter().noMoreAvailableSpaceForArgument(this.scope.locals[i], this.scope.locals[i].declaration);
}
}
}
@Override
public CompilationUnitDeclaration getCompilationUnitDeclaration() {
if (this.scope != null) {
return this.scope.compilationUnitScope().referenceContext;
}
return null;
}
@Override
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
public boolean isAbstract() {
if (this.binding != null)
return this.binding.isAbstract();
return (this.modifiers & ClassFileConstants.AccAbstract) != 0;
}
public boolean isAnnotationMethod() {
return false;
}
public boolean isClinit() {
return false;
}
public boolean isConstructor() {
return false;
}
public boolean isCanonicalConstructor() {
return false;
}
public boolean isDefaultConstructor() {
return false;
}
public boolean isDefaultMethod() {
return false;
}
public boolean isInitializationMethod() {
return false;
}
public boolean isMethod() {
return false;
}
public boolean isNative() {
if (this.binding != null)
return this.binding.isNative();
return (this.modifiers & ClassFileConstants.AccNative) != 0;
}
public RecordComponent getRecordComponent() {
return null;
}
public boolean isStatic() {
if (this.binding != null)
return this.binding.isStatic();
return (this.modifiers & ClassFileConstants.AccStatic) != 0;
}
/**
* Fill up the method body with statement
* @param parser
* @param unit
*/
public abstract void parseStatements(Parser parser, CompilationUnitDeclaration unit);
@Override
public StringBuffer print(int tab, StringBuffer output) {
if (this.javadoc != null) {
this.javadoc.print(tab, output);
}
printIndent(tab, output);
printModifiers(this.modifiers, output);
if (this.annotations != null) {
printAnnotations(this.annotations, output);
output.append(' ');
}
TypeParameter[] typeParams = typeParameters();
if (typeParams != null) {
output.append('<');
int max = typeParams.length - 1;
for (int j = 0; j < max; j++) {
typeParams[j].print(0, output);
output.append(", ");//$NON-NLS-1$
}
typeParams[max].print(0, output);
output.append('>');
}
printReturnType(0, output).append(this.selector).append('(');
if (this.receiver != null) {
this.receiver.print(0, output);
}
if (this.arguments != null) {
for (int i = 0; i < this.arguments.length; i++) {
if (i > 0 || this.receiver != null) output.append(", "); //$NON-NLS-1$
this.arguments[i].print(0, output);
}
}
output.append(')');
if (this.thrownExceptions != null) {
output.append(" throws "); //$NON-NLS-1$
for (int i = 0; i < this.thrownExceptions.length; i++) {
if (i > 0) output.append(", "); //$NON-NLS-1$
this.thrownExceptions[i].print(0, output);
}
}
printBody(tab + 1, output);
return output;
}
public StringBuffer printBody(int indent, StringBuffer output) {
if (isAbstract() || (this.modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0)
return output.append(';');
output.append(" {"); //$NON-NLS-1$
if (this.statements != null) {
for (int i = 0; i < this.statements.length; i++) {
output.append('\n');
this.statements[i].printStatement(indent, output);
}
}
output.append('\n');
printIndent(indent == 0 ? 0 : indent - 1, output).append('}');
return output;
}
public StringBuffer printReturnType(int indent, StringBuffer output) {
return output;
}
public void resolve(ClassScope upperScope) {
if (this.binding == null) {
this.ignoreFurtherInvestigation = true;
}
try {
bindArguments();
resolveReceiver();
bindThrownExceptions();
resolveAnnotations(this.scope, this.annotations, this.binding, this.isConstructor());
long sourceLevel = this.scope.compilerOptions().sourceLevel;
if (sourceLevel < ClassFileConstants.JDK1_8) // otherwise already checked via Argument.createBinding
validateNullAnnotations(this.scope.environment().usesNullTypeAnnotations());
resolveStatements();
// check @Deprecated annotation presence
if (this.binding != null
&& (this.binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) == 0
&& (this.binding.modifiers & ClassFileConstants.AccDeprecated) != 0
&& sourceLevel >= ClassFileConstants.JDK1_5) {
this.scope.problemReporter().missingDeprecatedAnnotationForMethod(this);
}
} catch (AbortMethod e) {
// ========= abort on fatal error =============
this.ignoreFurtherInvestigation = true;
}
}
public void resolveReceiver() {
if (this.receiver == null) return;
if (this.receiver.modifiers != 0) {
this.scope.problemReporter().illegalModifiers(this.receiver.declarationSourceStart, this.receiver.declarationSourceEnd);
}
TypeBinding resolvedReceiverType = this.receiver.type.resolvedType;
if (this.binding == null || resolvedReceiverType == null || !resolvedReceiverType.isValidBinding()) {
return;
}
ReferenceBinding declaringClass = this.binding.declaringClass;
/* neither static methods nor methods in anonymous types can have explicit 'this' */
if (this.isStatic() || declaringClass.isAnonymousType()) {
this.scope.problemReporter().disallowedThisParameter(this.receiver);
return; // No need to do further validation
}
ReferenceBinding enclosingReceiver = this.scope.enclosingReceiverType();
if (this.isConstructor()) {
/* Only non static member types or local types can declare explicit 'this' params in constructors */
if (declaringClass.isStatic()
|| (declaringClass.tagBits & (TagBits.IsLocalType | TagBits.IsMemberType)) == 0) { /* neither member nor local type */
this.scope.problemReporter().disallowedThisParameter(this.receiver);
return; // No need to do further validation
}
enclosingReceiver = enclosingReceiver.enclosingType();
}
char[][] tokens = (this.receiver.qualifyingName == null) ? null : this.receiver.qualifyingName.getName();
if (this.isConstructor()) {
if (tokens == null || tokens.length > 1 || !CharOperation.equals(enclosingReceiver.sourceName(), tokens[0])) {
this.scope.problemReporter().illegalQualifierForExplicitThis(this.receiver, enclosingReceiver);
this.receiver.qualifyingName = null;
}
} else if (tokens != null && tokens.length > 0) {
this.scope.problemReporter().illegalQualifierForExplicitThis2(this.receiver);
this.receiver.qualifyingName = null;
}
if (TypeBinding.notEquals(enclosingReceiver, resolvedReceiverType)) {
this.scope.problemReporter().illegalTypeForExplicitThis(this.receiver, enclosingReceiver);
}
if (this.receiver.type.hasNullTypeAnnotation(AnnotationPosition.ANY)) {
this.scope.problemReporter().nullAnnotationUnsupportedLocation(this.receiver.type);
}
}
public void resolveJavadoc() {
if (this.binding == null) return;
if (this.javadoc != null) {
this.javadoc.resolve(this.scope);
return;
}
if (this.binding.declaringClass != null && !this.binding.declaringClass.isLocalType()) {
// Set javadoc visibility
int javadocVisibility = this.binding.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
ClassScope classScope = this.scope.classScope();
ProblemReporter reporter = this.scope.problemReporter();
int severity = reporter.computeSeverity(IProblem.JavadocMissing);
if (severity != ProblemSeverities.Ignore) {
if (classScope != null) {
javadocVisibility = Util.computeOuterMostVisibility(classScope.referenceType(), javadocVisibility);
}
int javadocModifiers = (this.binding.modifiers & ~ExtraCompilerModifiers.AccVisibilityMASK) | javadocVisibility;
reporter.javadocMissing(this.sourceStart, this.sourceEnd, severity, javadocModifiers);
}
}
}
public void resolveStatements() {
if (this.statements != null) {
for (int i = 0, length = this.statements.length; i < length; i++) {
Statement stmt = this.statements[i];
stmt.resolve(this.scope);
}
} else if ((this.bits & UndocumentedEmptyBlock) != 0) {
if (!this.isConstructor() || this.arguments != null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=319626
this.scope.problemReporter().undocumentedEmptyBlock(this.bodyStart-1, this.bodyEnd+1);
}
}
}
@Override
public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
}
@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}
public void traverse(
ASTVisitor visitor,
ClassScope classScope) {
// default implementation: subclass will define it
}
public TypeParameter[] typeParameters() {
return null;
}
void validateNullAnnotations(boolean useTypeAnnotations) {
if (this.binding == null) return;
// null annotations on parameters?
if (!useTypeAnnotations) {
if (this.binding.parameterNonNullness != null) {
int length = this.binding.parameters.length;
for (int i=0; i