* Copyright (c) 2000, 2017 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 349326 - [1.7] new warning for missing try-with-resources bug 374605 - Unreasonable warning for enum-based switch statements bug 382353 - [1.8][compiler] Implementation property modifiers should be accepted on default methods. bug 382354 - [1.8][compiler] Compiler silent on conflicting modifier bug 401030 - [1.8][null] Null analysis support for lambda methods. Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault Jesper S Moller - Contributions for bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression
/******************************************************************************* * * Copyright (c) 2000, 2017 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 349326 - [1.7] new warning for missing try-with-resources * bug 374605 - Unreasonable warning for enum-based switch statements * bug 382353 - [1.8][compiler] Implementation property modifiers should be accepted on default methods. * bug 382354 - [1.8][compiler] Compiler silent on conflicting modifier * bug 401030 - [1.8][null] Null analysis support for lambda methods. * Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault * Jesper S Moller - Contributions for * bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
Specific block scope used for methods, constructors or clinits, representing its outermost blockscope. Note also that such a scope will be provided to enclose field initializers subscopes as well.
/** * Specific block scope used for methods, constructors or clinits, representing * its outermost blockscope. Note also that such a scope will be provided to enclose * field initializers subscopes as well. */
public class MethodScope extends BlockScope { public ReferenceContext referenceContext; public boolean isStatic; // method modifier or initializer one //fields used during name resolution public boolean isConstructorCall = false; public FieldBinding initializedField; // the field being initialized public int lastVisibleFieldID = -1; // the ID of the last field which got declared // note that #initializedField can be null AND lastVisibleFieldID >= 0, when processing instance field initializers. // flow analysis public int analysisIndex; // for setting flow-analysis id public boolean isPropagatingInnerClassEmulation; // for local variables table attributes public int lastIndex = 0; public long[] definiteInits = new long[4]; public long[][] extraDefiniteInits = new long[4][]; // inner-emulation public SyntheticArgumentBinding[] extraSyntheticArguments; // remember suppressed warning re missing 'default:' to give hints on possibly related flow problems public boolean hasMissingSwitchDefault; // TODO(stephan): combine flags to a bitset? public MethodScope(Scope parent, ReferenceContext context, boolean isStatic) { super(METHOD_SCOPE, parent); this.locals = new LocalVariableBinding[5]; this.referenceContext = context; this.isStatic = isStatic; this.startIndex = 0; } public MethodScope(Scope parent, ReferenceContext context, boolean isStatic, int lastVisibleFieldID) { this(parent, context, isStatic); this.lastVisibleFieldID = lastVisibleFieldID; } @Override String basicToString(int tab) { String newLine = "\n"; //$NON-NLS-1$ for (int i = tab; --i >= 0;) newLine += "\t"; //$NON-NLS-1$ String s = newLine + "--- Method Scope ---"; //$NON-NLS-1$ newLine += "\t"; //$NON-NLS-1$ s += newLine + "locals:"; //$NON-NLS-1$ for (int i = 0; i < this.localIndex; i++) s += newLine + "\t" + this.locals[i].toString(); //$NON-NLS-1$ s += newLine + "startIndex = " + this.startIndex; //$NON-NLS-1$ s += newLine + "isConstructorCall = " + this.isConstructorCall; //$NON-NLS-1$ s += newLine + "initializedField = " + this.initializedField; //$NON-NLS-1$ s += newLine + "lastVisibleFieldID = " + this.lastVisibleFieldID; //$NON-NLS-1$ s += newLine + "referenceContext = " + this.referenceContext; //$NON-NLS-1$ return s; }
Spec : 8.4.3 & 9.4
/** * Spec : 8.4.3 & 9.4 */
private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) { int modifiers = methodBinding.modifiers; final ReferenceBinding declaringClass = methodBinding.declaringClass; if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) problemReporter().duplicateModifierForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); if ((((ConstructorDeclaration) this.referenceContext).bits & ASTNode.IsDefaultConstructor) != 0) { // certain flags are propagated from declaring class onto constructor final int DECLARING_FLAGS = ClassFileConstants.AccEnum|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; final int VISIBILITY_FLAGS = ClassFileConstants.AccPrivate|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; int flags; if ((flags = declaringClass.modifiers & DECLARING_FLAGS) != 0) { if ((flags & ClassFileConstants.AccEnum) != 0) { modifiers &= ~VISIBILITY_FLAGS; modifiers |= ClassFileConstants.AccPrivate; // default constructor is implicitly private in enum } else { modifiers &= ~VISIBILITY_FLAGS; modifiers |= flags; // propagate public/protected } } } // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; // check for abnormal modifiers final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStrictfp); if (declaringClass.isEnum() && (((ConstructorDeclaration) this.referenceContext).bits & ASTNode.IsDefaultConstructor) == 0) { final int UNEXPECTED_ENUM_CONSTR_MODIFIERS = ~(ClassFileConstants.AccPrivate | ClassFileConstants.AccStrictfp); if ((realModifiers & UNEXPECTED_ENUM_CONSTR_MODIFIERS) != 0) { problemReporter().illegalModifierForEnumConstructor((AbstractMethodDeclaration) this.referenceContext); modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_ENUM_CONSTR_MODIFIERS; } else if ((((AbstractMethodDeclaration) this.referenceContext).modifiers & ClassFileConstants.AccStrictfp) != 0) { // must check the parse node explicitly problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); } modifiers |= ClassFileConstants.AccPrivate; // enum constructor is implicitly private } else if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_MODIFIERS; } else if ((((AbstractMethodDeclaration) this.referenceContext).modifiers & ClassFileConstants.AccStrictfp) != 0) { // must check the parse node explicitly problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); } // check for incompatible modifiers in the visibility bits, isolate the visibility bits int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) != 0) { problemReporter().illegalVisibilityModifierCombinationForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { modifiers &= ~ClassFileConstants.AccPrivate; } } // // if the receiver's declaring class is a private nested type, then make sure the receiver is not private (causes problems for inner type emulation) // if (declaringClass.isPrivate() && (modifiers & ClassFileConstants.AccPrivate) != 0) // modifiers &= ~ClassFileConstants.AccPrivate; methodBinding.modifiers = modifiers; }
Spec : 8.4.3 & 9.4 TODO: Add the spec section number for private interface methods from jls 9
/** * Spec : 8.4.3 & 9.4 * TODO: Add the spec section number for private interface methods from jls 9 */
private void checkAndSetModifiersForMethod(MethodBinding methodBinding) { int modifiers = methodBinding.modifiers; final ReferenceBinding declaringClass = methodBinding.declaringClass; if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) problemReporter().duplicateModifierForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; long sourceLevel = compilerOptions().sourceLevel; // set the requested modifiers for a method in an interface/annotation if (declaringClass.isInterface()) { int expectedModifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; boolean isDefaultMethod = (modifiers & ExtraCompilerModifiers.AccDefaultMethod) != 0; // no need to check validity, is done by the parser boolean reportIllegalModifierCombination = false; if (sourceLevel >= ClassFileConstants.JDK1_8 && !declaringClass.isAnnotationType()) { expectedModifiers |= ClassFileConstants.AccStrictfp | ExtraCompilerModifiers.AccDefaultMethod | ClassFileConstants.AccStatic; expectedModifiers |= sourceLevel >= ClassFileConstants.JDK9 ? ClassFileConstants.AccPrivate : 0; if (!methodBinding.isAbstract()) { reportIllegalModifierCombination = isDefaultMethod && methodBinding.isStatic(); } else { reportIllegalModifierCombination = isDefaultMethod || methodBinding.isStatic(); if (methodBinding.isStrictfp()) { problemReporter().illegalAbstractModifierCombinationForMethod((AbstractMethodDeclaration) this.referenceContext); } } if (reportIllegalModifierCombination) { problemReporter().illegalModifierCombinationForInterfaceMethod((AbstractMethodDeclaration) this.referenceContext); } if (sourceLevel >= ClassFileConstants.JDK9 && (methodBinding.modifiers & ClassFileConstants.AccPrivate) != 0) { int remaining = realModifiers & ~expectedModifiers; if (remaining == 0) { // check for the combination of allowed modifiers with private remaining = realModifiers & ~(ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccStrictfp); if (isDefaultMethod || remaining != 0) problemReporter().illegalModifierCombinationForPrivateInterfaceMethod((AbstractMethodDeclaration) this.referenceContext); } } // Kludge - The AccDefaultMethod bit is outside the lower 16 bits and got removed earlier. Putting it back. if (isDefaultMethod) { realModifiers |= ExtraCompilerModifiers.AccDefaultMethod; } } if ((realModifiers & ~expectedModifiers) != 0) { if ((declaringClass.modifiers & ClassFileConstants.AccAnnotation) != 0) problemReporter().illegalModifierForAnnotationMember((AbstractMethodDeclaration) this.referenceContext); else problemReporter().illegalModifierForInterfaceMethod((AbstractMethodDeclaration) this.referenceContext, sourceLevel); methodBinding.modifiers &= (expectedModifiers | ~ExtraCompilerModifiers.AccJustFlag); } return; } else if (declaringClass.isAnonymousType() && sourceLevel >= ClassFileConstants.JDK9) { // If the class instance creation expression elides the supertype's type arguments using '<>', // then for all non-private methods declared in the class body, it is as if the method declaration // is annotated with @Override - https://bugs.openjdk.java.net/browse/JDK-8073593 LocalTypeBinding local = (LocalTypeBinding) declaringClass; TypeReference ref = local.scope.referenceContext.allocation.type; if (ref != null && (ref.bits & ASTNode.IsDiamond) != 0) { // if ((realModifiers & (ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic )) == 0) { methodBinding.tagBits |= TagBits.AnnotationOverride; } } } // check for abnormal modifiers final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccAbstract | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_MODIFIERS; } // check for incompatible modifiers in the visibility bits, isolate the visibility bits int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) != 0) { problemReporter().illegalVisibilityModifierCombinationForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { modifiers &= ~ClassFileConstants.AccPrivate; } } // check for modifiers incompatible with abstract modifier if ((modifiers & ClassFileConstants.AccAbstract) != 0) { int incompatibleWithAbstract = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp; if ((modifiers & incompatibleWithAbstract) != 0) problemReporter().illegalAbstractModifierCombinationForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); if (!methodBinding.declaringClass.isAbstract()) problemReporter().abstractMethodInAbstractClass((SourceTypeBinding) declaringClass, (AbstractMethodDeclaration) this.referenceContext); } /* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final) // methods from a final class are final : 8.4.3.3 if (methodBinding.declaringClass.isFinal()) modifiers |= AccFinal; */ // native methods cannot also be tagged as strictfp if ((modifiers & ClassFileConstants.AccNative) != 0 && (modifiers & ClassFileConstants.AccStrictfp) != 0) problemReporter().nativeMethodsCannotBeStrictfp(declaringClass, (AbstractMethodDeclaration) this.referenceContext); // static members are only authorized in a static member or top level type if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic()) problemReporter().unexpectedStaticModifierForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); methodBinding.modifiers = modifiers; } public void checkUnusedParameters(MethodBinding method) { if (method.isAbstract() || (method.isImplementing() && !compilerOptions().reportUnusedParameterWhenImplementingAbstract) || (method.isOverriding() && !method.isImplementing() && !compilerOptions().reportUnusedParameterWhenOverridingConcrete) || method.isMain()) { // do not want to check return; } for (int i = 0, maxLocals = this.localIndex; i < maxLocals; i++) { LocalVariableBinding local = this.locals[i]; if (local == null || ((local.tagBits & TagBits.IsArgument) == 0)) { break; // done with arguments } if (local.useFlag == LocalVariableBinding.UNUSED && // do not report fake used variable ((local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 0)) { // declaration is reachable problemReporter().unusedArgument(local.declaration); } } }
Compute variable positions in scopes given an initial position offset ignoring unused local variables. Deal with arguments here, locals and subscopes are processed in BlockScope method
/** * Compute variable positions in scopes given an initial position offset * ignoring unused local variables. * * Deal with arguments here, locals and subscopes are processed in BlockScope method */
public void computeLocalVariablePositions(int initOffset, CodeStream codeStream) { this.offset = initOffset; this.maxOffset = initOffset; // manage arguments int ilocal = 0, maxLocals = this.localIndex; while (ilocal < maxLocals) { LocalVariableBinding local = this.locals[ilocal]; if (local == null || ((local.tagBits & TagBits.IsArgument) == 0)) break; // done with arguments // record user-defined argument for attribute generation codeStream.record(local); // assign variable position local.resolvedPosition = this.offset; if ((TypeBinding.equalsEquals(local.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(local.type, TypeBinding.DOUBLE))) { this.offset += 2; } else { this.offset++; } // check for too many arguments/local variables if (this.offset > 0xFF) { // no more than 255 words of arguments problemReporter().noMoreAvailableSpaceForArgument(local, local.declaration); } ilocal++; } // sneak in extra argument before other local variables if (this.extraSyntheticArguments != null) { for (int iarg = 0, maxArguments = this.extraSyntheticArguments.length; iarg < maxArguments; iarg++){ SyntheticArgumentBinding argument = this.extraSyntheticArguments[iarg]; argument.resolvedPosition = this.offset; if ((TypeBinding.equalsEquals(argument.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(argument.type, TypeBinding.DOUBLE))){ this.offset += 2; } else { this.offset++; } if (this.offset > 0xFF) { // no more than 255 words of arguments problemReporter().noMoreAvailableSpaceForArgument(argument, (ASTNode)this.referenceContext); } } } this.computeLocalVariablePositions(ilocal, this.offset, codeStream); }
Error management: keep null for all the errors that prevent the method to be created otherwise return a correct method binding (but without the element that caused the problem) : i.e. Incorrect thrown exception
/** * Error management: * keep null for all the errors that prevent the method to be created * otherwise return a correct method binding (but without the element * that caused the problem) : i.e. Incorrect thrown exception */
MethodBinding createMethod(AbstractMethodDeclaration method) { // is necessary to ensure error reporting this.referenceContext = method; method.scope = this; long sourceLevel = compilerOptions().sourceLevel; SourceTypeBinding declaringClass = referenceType().binding; int modifiers = method.modifiers | ExtraCompilerModifiers.AccUnresolved; if (method.isConstructor()) { if (method.isDefaultConstructor()) modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor; method.binding = new MethodBinding(modifiers, null, null, declaringClass); checkAndSetModifiersForConstructor(method.binding); } else { if (declaringClass.isInterface()) {// interface or annotation type if (sourceLevel >= ClassFileConstants.JDK9 && ((method.modifiers & ClassFileConstants.AccPrivate) != 0)) { // private method // do nothing } else if (method.isDefaultMethod() || method.isStatic()) { modifiers |= ClassFileConstants.AccPublic; // default method is not abstract } else { modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; } } method.binding = new MethodBinding(modifiers, method.selector, null, null, null, declaringClass); checkAndSetModifiersForMethod(method.binding); } this.isStatic = method.binding.isStatic(); Argument[] argTypes = method.arguments; int argLength = argTypes == null ? 0 : argTypes.length; if (argLength > 0) { Argument argument = argTypes[--argLength]; if (argument.isVarArgs() && sourceLevel >= ClassFileConstants.JDK1_5) method.binding.modifiers |= ClassFileConstants.AccVarargs; if (CharOperation.equals(argument.name, ConstantPool.This)) { problemReporter().illegalThisDeclaration(argument); } while (--argLength >= 0) { argument = argTypes[argLength]; if (argument.isVarArgs() && sourceLevel >= ClassFileConstants.JDK1_5) problemReporter().illegalVararg(argument, method); if (CharOperation.equals(argument.name, ConstantPool.This)) { problemReporter().illegalThisDeclaration(argument); } } } if (method.receiver != null) { if (sourceLevel <= ClassFileConstants.JDK1_7) { problemReporter().illegalSourceLevelForThis(method.receiver); } if (method.receiver.annotations != null) { method.bits |= ASTNode.HasTypeAnnotations; } } TypeParameter[] typeParameters = method.typeParameters(); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, If they exist at all, process type parameters irrespective of source level. if (typeParameters == null || typeParameters.length == 0) { method.binding.typeVariables = Binding.NO_TYPE_VARIABLES; } else { method.binding.typeVariables = createTypeVariables(typeParameters, method.binding); method.binding.modifiers |= ExtraCompilerModifiers.AccGenericSignature; } return method.binding; }
Overridden to detect the error case inside an explicit constructor call: class X { int i; X myX; X(X x) { this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors } }
/** * Overridden to detect the error case inside an explicit constructor call: class X { int i; X myX; X(X x) { this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors } } */
@Override public FieldBinding findField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite, boolean needResolve) { FieldBinding field = super.findField(receiverType, fieldName, invocationSite, needResolve); if (field == null) return null; if (!field.isValidBinding()) return field; // answer the error field if (receiverType.isInterface() && invocationSite.isQualifiedSuper()) return new ProblemFieldBinding( field, // closest match field.declaringClass, fieldName, ProblemReasons.NoProperEnclosingInstance); if (field.isStatic()) return field; // static fields are always accessible if (!this.isConstructorCall || TypeBinding.notEquals(receiverType, enclosingSourceType())) return field; if (invocationSite instanceof SingleNameReference) return new ProblemFieldBinding( field, // closest match field.declaringClass, fieldName, ProblemReasons.NonStaticReferenceInConstructorInvocation); if (invocationSite instanceof QualifiedNameReference) { // look to see if the field is the first binding QualifiedNameReference name = (QualifiedNameReference) invocationSite; if (name.binding == null) // only true when the field is the fieldbinding at the beginning of name's tokens return new ProblemFieldBinding( field, // closest match field.declaringClass, fieldName, ProblemReasons.NonStaticReferenceInConstructorInvocation); } return field; } public boolean isInsideConstructor() { return (this.referenceContext instanceof ConstructorDeclaration); } public boolean isInsideInitializer() { return (this.referenceContext instanceof TypeDeclaration); } @Override public boolean isLambdaScope() { return this.referenceContext instanceof LambdaExpression; } public boolean isInsideInitializerOrConstructor() { return (this.referenceContext instanceof TypeDeclaration) || (this.referenceContext instanceof ConstructorDeclaration); }
Answer the problem reporter to use for raising new problems. Note that as a side-effect, this updates the current reference context (unit, type or method) in case the problem handler decides it is necessary to abort.
/** * Answer the problem reporter to use for raising new problems. * * Note that as a side-effect, this updates the current reference context * (unit, type or method) in case the problem handler decides it is necessary * to abort. */
@Override public ProblemReporter problemReporter() { ProblemReporter problemReporter = referenceCompilationUnit().problemReporter; problemReporter.referenceContext = this.referenceContext; return problemReporter; } public final int recordInitializationStates(FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return -1; UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect(); long[] extraInits = unconditionalFlowInfo.extra == null ? null : unconditionalFlowInfo.extra[0]; long inits = unconditionalFlowInfo.definiteInits; checkNextEntry : for (int i = this.lastIndex; --i >= 0;) { if (this.definiteInits[i] == inits) { long[] otherInits = this.extraDefiniteInits[i]; if ((extraInits != null) && (otherInits != null)) { if (extraInits.length == otherInits.length) { int j, max; for (j = 0, max = extraInits.length; j < max; j++) { if (extraInits[j] != otherInits[j]) { continue checkNextEntry; } } return i; } } else { if ((extraInits == null) && (otherInits == null)) { return i; } } } } // add a new entry if (this.definiteInits.length == this.lastIndex) { // need a resize System.arraycopy( this.definiteInits, 0, (this.definiteInits = new long[this.lastIndex + 20]), 0, this.lastIndex); System.arraycopy( this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[this.lastIndex + 20][]), 0, this.lastIndex); } this.definiteInits[this.lastIndex] = inits; if (extraInits != null) { this.extraDefiniteInits[this.lastIndex] = new long[extraInits.length]; System.arraycopy( extraInits, 0, this.extraDefiniteInits[this.lastIndex], 0, extraInits.length); } return this.lastIndex++; }
Answer the reference method of this scope, or null if initialization scope or lambda scope.
/** * Answer the reference method of this scope, or null if initialization scope or lambda scope. */
public AbstractMethodDeclaration referenceMethod() { if (this.referenceContext instanceof AbstractMethodDeclaration) return (AbstractMethodDeclaration) this.referenceContext; return null; }
Answers the binding of the reference method or reference lambda expression.
/** * Answers the binding of the reference method or reference lambda expression. */
public MethodBinding referenceMethodBinding() { if (this.referenceContext instanceof LambdaExpression) return ((LambdaExpression)this.referenceContext).binding; if (this.referenceContext instanceof AbstractMethodDeclaration) return ((AbstractMethodDeclaration)this.referenceContext).binding; return null; }
Answer the reference type of this scope. It is the nearest enclosing type of this scope.
/** * Answer the reference type of this scope. * It is the nearest enclosing type of this scope. */
@Override public TypeDeclaration referenceType() { ClassScope scope = enclosingClassScope(); return scope == null ? null : scope.referenceContext; } @Override void resolveTypeParameter(TypeParameter typeParameter) { typeParameter.resolve(this); } @Override public boolean hasDefaultNullnessFor(int location, int sourceStart) { int nonNullByDefaultValue = localNonNullByDefaultValue(sourceStart); if(nonNullByDefaultValue != 0) { return (nonNullByDefaultValue & location) != 0; } AbstractMethodDeclaration referenceMethod = referenceMethod(); if (referenceMethod != null) { MethodBinding binding = referenceMethod.binding; if (binding != null && binding.defaultNullness != 0) { return (binding.defaultNullness & location) != 0; } } return this.parent.hasDefaultNullnessFor(location, sourceStart); } @Override public Binding checkRedundantDefaultNullness(int nullBits, int sourceStart) { Binding target = localCheckRedundantDefaultNullness(nullBits, sourceStart); if (target != null) { return target; } AbstractMethodDeclaration referenceMethod = referenceMethod(); if (referenceMethod != null) { MethodBinding binding = referenceMethod.binding; if (binding != null && binding.defaultNullness != 0) { return (binding.defaultNullness == nullBits) ? binding : null; } } return this.parent.checkRedundantDefaultNullness(nullBits, sourceStart); } public boolean shouldCheckAPILeaks(ReferenceBinding declaringClass, boolean memberIsPublic) { if (environment().useModuleSystem) return memberIsPublic && declaringClass.isPublic() && declaringClass.fPackage.isExported(); return false; } public void detectAPILeaks(ASTNode typeNode, TypeBinding type) { if (environment().useModuleSystem) { // NB: using an ASTVisitor yields more precise locations than a TypeBindingVisitor would ASTVisitor visitor = new ASTVisitor() { @Override public boolean visit(SingleTypeReference typeReference, BlockScope scope) { if (typeReference.resolvedType instanceof ReferenceBinding) checkType((ReferenceBinding) typeReference.resolvedType, typeReference.sourceStart, typeReference.sourceEnd); return true; } @Override public boolean visit(QualifiedTypeReference typeReference, BlockScope scope) { if (typeReference.resolvedType instanceof ReferenceBinding) checkType((ReferenceBinding) typeReference.resolvedType, typeReference.sourceStart, typeReference.sourceEnd); return true; } @Override public boolean visit(ArrayTypeReference typeReference, BlockScope scope) { TypeBinding leafComponentType = typeReference.resolvedType.leafComponentType(); if (leafComponentType instanceof ReferenceBinding) checkType((ReferenceBinding) leafComponentType, typeReference.sourceStart, typeReference.originalSourceEnd); return true; } private void checkType(ReferenceBinding referenceBinding, int sourceStart, int sourceEnd) { if (!referenceBinding.isValidBinding()) return; ModuleBinding otherModule = referenceBinding.module(); if (otherModule == otherModule.environment.javaBaseModule()) return; // always accessible if (!isFullyPublic(referenceBinding)) { problemReporter().nonPublicTypeInAPI(referenceBinding, sourceStart, sourceEnd); } else if (!referenceBinding.fPackage.isExported()) { problemReporter().notExportedTypeInAPI(referenceBinding, sourceStart, sourceEnd); } else if (isUnrelatedModule(referenceBinding.fPackage)) { problemReporter().missingRequiresTransitiveForTypeInAPI(referenceBinding, sourceStart, sourceEnd); } } private boolean isFullyPublic(ReferenceBinding referenceBinding) { if (!referenceBinding.isPublic()) return false; if (referenceBinding instanceof NestedTypeBinding) return isFullyPublic(((NestedTypeBinding) referenceBinding).enclosingType); return true; } private boolean isUnrelatedModule(PackageBinding fPackage) { ModuleBinding otherModule = fPackage.enclosingModule; ModuleBinding thisModule = module(); if (thisModule != otherModule) { return !thisModule.isTransitivelyRequired(otherModule); } return false; } }; typeNode.traverse(visitor, this); } } }