blob: 7d500e5a3a369ff865955f2e75c7205fa81e1e55 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
* Stephan Herrmann - Contribution for bug 295551
* Jesper S Moller - Contributions for
* Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
* Frits Jalvingh - contributions for bug 533830.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import java.util.Arrays;
import java.util.Comparator;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.Compiler;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.IrritantSet;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.parser.NLSTag;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
import org.eclipse.jdt.internal.compiler.problem.AbortType;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.HashSetOfInt;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateMemento;
/**
* OTDT changes:
*
* ROLE UNITS:
* What: link to CUD of enclosing team
* Why: it should be possible to find both the physically containing (RoleUnit)
* as well as logically containing (team) CUD.
*
* DECOUPLE FROM COMPILER:
* What: store index into Compiler.unitsToProcess
* What: Let resolve only be called by Dependencies.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class CompilationUnitDeclaration extends ASTNode implements ProblemSeverities, ReferenceContext {
private static final Comparator STRING_LITERAL_COMPARATOR = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
StringLiteral literal1 = (StringLiteral) o1;
StringLiteral literal2 = (StringLiteral) o2;
return literal1.sourceStart - literal2.sourceStart;
}
};
private static final int STRING_LITERALS_INCREMENT = 10;
public ImportReference currentPackage;
public ImportReference[] imports;
public TypeDeclaration[] types;
public ModuleDeclaration moduleDeclaration;
public int[][] comments;
public boolean ignoreFurtherInvestigation = false; // once pointless to investigate due to errors
public boolean ignoreMethodBodies = false;
public CompilationUnitScope scope;
public ProblemReporter problemReporter;
public CompilationResult compilationResult;
public LocalTypeBinding[] localTypes;
public int localTypeCount = 0;
//{ObjectTeams:
// store this index so we can invoke getMethodBodies from outside Compiler
public int place; // index into Compiler.unitsToProcess
// State with respect to Dependencies:
public final StateMemento state= new StateMemento();
// for some CUD we need no method bodies. This is different from ignoreMethodBodies,
// because the latter seems to relate to syntax errors, whereas this flag is set for
// a CUD that created by the SourceTypeConverter (and is seemingly needed only indirectly)
public boolean parseMethodBodies = true;
// back reference needed as 'client' for Config:
public Compiler compiler;
// SH}
public boolean isPropagatingInnerClassEmulation;
public Javadoc javadoc; // 1.5 addition for package-info.java
public NLSTag[] nlsTags;
private StringLiteral[] stringLiterals;
private int stringLiteralsPtr;
private HashSetOfInt stringLiteralsStart;
public boolean[] validIdentityComparisonLines;
IrritantSet[] suppressWarningIrritants; // irritant for suppressed warnings
Annotation[] suppressWarningAnnotations;
long[] suppressWarningScopePositions; // (start << 32) + end
int suppressWarningsCount;
public int functionalExpressionsCount;
public FunctionalExpression[] functionalExpressions;
public CompilationUnitDeclaration(ProblemReporter problemReporter, CompilationResult compilationResult, int sourceLength) {
this.problemReporter = problemReporter;
this.compilationResult = compilationResult;
//by definition of a compilation unit....
this.sourceStart = 0;
this.sourceEnd = sourceLength - 1;
}
/*
* We cause the compilation task to abort to a given extent.
*/
@Override
public void abort(int abortLevel, CategorizedProblem problem) {
switch (abortLevel) {
case AbortType :
throw new AbortType(this.compilationResult, problem);
case AbortMethod :
throw new AbortMethod(this.compilationResult, problem);
default :
throw new AbortCompilationUnit(this.compilationResult, problem);
}
}
/*
* Dispatch code analysis AND request saturation of inner emulation
*/
public void analyseCode() {
//{ObjectTeams: postponed from resolve() to also catch import usage from late statement generators.
if (!this.compilationResult.hasMandatoryErrors())
checkUnusedImports();
// SH}
if (this.ignoreFurtherInvestigation)
return;
try {
if (this.types != null) {
for (int i = 0, count = this.types.length; i < count; i++) {
this.types[i].analyseCode(this.scope);
}
}
if (this.moduleDeclaration != null) {
this.moduleDeclaration.analyseCode(this.scope);
}
// request inner emulation propagation
propagateInnerEmulationForAllLocalTypes();
} catch (AbortCompilationUnit e) {
this.ignoreFurtherInvestigation = true;
return;
}
}
/*
* When unit result is about to be accepted, removed back pointers
* to compiler structures.
*/
public void cleanUp() {
//{ObjectTeams: cleanup additional field:
this.compiler = null;
// SH}
if (this.types != null) {
for (int i = 0, max = this.types.length; i < max; i++) {
cleanUp(this.types[i]);
}
for (int i = 0, max = this.localTypeCount; i < max; i++) {
LocalTypeBinding localType = this.localTypes[i];
// null out the type's scope backpointers
localType.scope = null; // local members are already in the list
localType.enclosingCase = null;
}
}
if (this.functionalExpressionsCount > 0) {
for (int i = 0, max = this.functionalExpressionsCount; i < max; i++) {
this.functionalExpressions[i].cleanUp();
}
}
this.compilationResult.recoveryScannerData = null; // recovery is already done
ClassFile[] classFiles = this.compilationResult.getClassFiles();
for (int i = 0, max = classFiles.length; i < max; i++) {
// clear the classFile back pointer to the bindings
ClassFile classFile = classFiles[i];
//{ObjectTeams: should writeClassFile store the path of the class file written?
classFile.maybeRememberModel();
// SH}
// null out the classfile backpointer to a type binding
classFile.referenceBinding = null;
classFile.innerClassesBindings = null;
classFile.bootstrapMethods = null;
classFile.missingTypes = null;
classFile.visitedTypes = null;
}
this.suppressWarningAnnotations = null;
if (this.scope != null)
this.scope.cleanUpInferenceContexts();
}
private void cleanUp(TypeDeclaration type) {
if (type.memberTypes != null) {
for (int i = 0, max = type.memberTypes.length; i < max; i++){
//{ObjectTeams: ROFI: don't descend into role files which have their on CompilationUnitDeclaration:
if (!type.memberTypes[i].isRoleFile())
// SH}
cleanUp(type.memberTypes[i]);
}
}
if (type.binding != null && type.binding.isAnnotationType())
this.compilationResult.hasAnnotations = true;
if (type.binding != null) {
// null out the type's scope backpointers
type.binding.scope = null;
}
//{ObjectTeams: final cleanup also from us:
type.cleanupModels();
// SH}
}
public void checkUnusedImports(){
if (this.scope.imports != null){
for (int i = 0, max = this.scope.imports.length; i < max; i++){
ImportBinding importBinding = this.scope.imports[i];
ImportReference importReference = importBinding.reference;
if (importReference != null && ((importReference.bits & ASTNode.Used) == 0)){
this.scope.problemReporter().unusedImport(importReference);
}
}
}
}
@Override
public CompilationResult compilationResult() {
return this.compilationResult;
}
public void createPackageInfoType() {
TypeDeclaration declaration = new TypeDeclaration(this.compilationResult);
declaration.name = TypeConstants.PACKAGE_INFO_NAME;
declaration.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccInterface;
declaration.javadoc = this.javadoc;
this.types[0] = declaration; // Assumes the first slot is meant for this type
}
/*
* Finds the matching type amoung this compilation unit types.
* Returns null if no type with this name is found.
* The type name is a compound name
* e.g. if we're looking for X.A.B then a type name would be {X, A, B}
*/
public TypeDeclaration declarationOfType(char[][] typeName) {
for (int i = 0; i < this.types.length; i++) {
TypeDeclaration typeDecl = this.types[i].declarationOfType(typeName);
if (typeDecl != null) {
return typeDecl;
}
}
return null;
}
public void finalizeProblems() {
//{ObjectTeams: filter out over-eager problems:
IrritantSet[] foundIrritants = new IrritantSet[this.suppressWarningsCount];
this.compilationResult.recheckProblems(foundIrritants);
// orig:
int problemCount = this.compilationResult.problemCount;
CategorizedProblem[] problems = this.compilationResult.problems;
if (this.suppressWarningsCount == 0) {
return;
}
int removed = 0;
/*
IrritantSet[] foundIrritants = new IrritantSet[this.suppressWarningsCount];
:giro */
// SH}
CompilerOptions options = this.scope.compilerOptions();
boolean hasMandatoryErrors = false;
nextProblem: for (int iProblem = 0, length = problemCount; iProblem < length; iProblem++) {
CategorizedProblem problem = problems[iProblem];
int problemID = problem.getID();
int irritant = ProblemReporter.getIrritant(problemID);
boolean isError = problem.isError();
if (isError) {
if (irritant == 0) {
// tolerate unused warning tokens when mandatory errors
hasMandatoryErrors = true;
continue;
}
if (!options.suppressOptionalErrors) {
continue;
}
}
int start = problem.getSourceStart();
int end = problem.getSourceEnd();
nextSuppress: for (int iSuppress = 0, suppressCount = this.suppressWarningsCount; iSuppress < suppressCount; iSuppress++) {
long position = this.suppressWarningScopePositions[iSuppress];
int startSuppress = (int) (position >>> 32);
int endSuppress = (int) position;
if (start < startSuppress) continue nextSuppress;
if (end > endSuppress) continue nextSuppress;
if (!this.suppressWarningIrritants[iSuppress].isSet(irritant)) {
if (problem instanceof DefaultProblem) {
((DefaultProblem) problem).reportError();
}
continue nextSuppress;
}
// discard suppressed warning
removed++;
problems[iProblem] = null;
this.compilationResult.removeProblem(problem);
if (foundIrritants[iSuppress] == null){
foundIrritants[iSuppress] = new IrritantSet(irritant);
} else {
foundIrritants[iSuppress].set(irritant);
}
continue nextProblem;
}
}
//{ObjectTeams: also remove marked problems in generated code (no longer need it to match SuppressWarnings):
// (see also CompilationUnitProblemFinder#initializeParser)
for (int iProblem = 0, length = problemCount; iProblem < length; iProblem++) {
CategorizedProblem problem = problems[iProblem];
if (problem instanceof DefaultProblem) {
DefaultProblem defaultProblem = (DefaultProblem) problem;
if (defaultProblem.isInGenerated()) {
problems[iProblem] = null;
this.compilationResult.removeProblem(defaultProblem);
removed++;
}
}
}
// SH}
// compact remaining problems
if (removed > 0) {
for (int i = 0, index = 0; i < problemCount; i++) {
CategorizedProblem problem;
if ((problem = problems[i]) != null) {
if (i > index) {
problems[index++] = problem;
} else {
index++;
}
}
}
}
// flag SuppressWarnings which had no effect (only if no (mandatory) error got detected within unit
if (!hasMandatoryErrors) {
int severity = options.getSeverity(CompilerOptions.UnusedWarningToken);
if (severity != ProblemSeverities.Ignore) {
boolean unusedWarningTokenIsWarning = (severity & ProblemSeverities.Error) == 0;
for (int iSuppress = 0, suppressCount = this.suppressWarningsCount; iSuppress < suppressCount; iSuppress++) {
Annotation annotation = this.suppressWarningAnnotations[iSuppress];
if (annotation == null) continue; // implicit annotation
IrritantSet irritants = this.suppressWarningIrritants[iSuppress];
if (unusedWarningTokenIsWarning && irritants.areAllSet()) continue; // @SuppressWarnings("all") also suppresses unused warning token
//{ObjectTeams: don't report unused suppress against copy-inherited member:
switch (annotation.recipient.kind()) {
case Binding.METHOD:
if (((MethodBinding)annotation.recipient).copyInheritanceSrc != null)
continue;
break;
case Binding.FIELD:
if (((FieldBinding)annotation.recipient).copyInheritanceSrc != null)
continue;
break;
}
// SH}
if (irritants != foundIrritants[iSuppress]) { // mismatch, some warning tokens were unused
MemberValuePair[] pairs = annotation.memberValuePairs();
pairLoop: for (int iPair = 0, pairCount = pairs.length; iPair < pairCount; iPair++) {
MemberValuePair pair = pairs[iPair];
if (CharOperation.equals(pair.name, TypeConstants.VALUE)) {
Expression value = pair.value;
if (value instanceof ArrayInitializer) {
ArrayInitializer initializer = (ArrayInitializer) value;
Expression[] inits = initializer.expressions;
if (inits != null) {
for (int iToken = 0, tokenCount = inits.length; iToken < tokenCount; iToken++) {
Constant cst = inits[iToken].constant;
if (cst != Constant.NotAConstant && cst.typeID() == TypeIds.T_JavaLangString) {
IrritantSet tokenIrritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
if (tokenIrritants != null) {
if (!tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all")
&& (foundIrritants[iSuppress] == null || !foundIrritants[iSuppress].isAnySet(tokenIrritants))) { // if irritant had no matching problem
if (unusedWarningTokenIsWarning) {
int start = value.sourceStart, end = value.sourceEnd;
nextSuppress: for (int jSuppress = iSuppress - 1; jSuppress >= 0; jSuppress--) {
long position = this.suppressWarningScopePositions[jSuppress];
int startSuppress = (int) (position >>> 32);
int endSuppress = (int) position;
if (start < startSuppress) continue nextSuppress;
if (end > endSuppress) continue nextSuppress;
if (this.suppressWarningIrritants[jSuppress].areAllSet()) break pairLoop; // suppress all?
}
}
int id = options.getIgnoredIrritant(tokenIrritants);
if (id > 0) {
String key = CompilerOptions.optionKeyFromIrritant(id);
this.scope.problemReporter().problemNotAnalysed(inits[iToken], key);
} else {
this.scope.problemReporter().unusedWarningToken(inits[iToken]);
}
}
}
}
}
}
} else {
Constant cst = value.constant;
if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
IrritantSet tokenIrritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
if (tokenIrritants != null) {
if (!tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all")
&& (foundIrritants[iSuppress] == null || !foundIrritants[iSuppress].isAnySet(tokenIrritants))) { // if irritant had no matching problem
if (unusedWarningTokenIsWarning) {
int start = value.sourceStart, end = value.sourceEnd;
nextSuppress: for (int jSuppress = iSuppress - 1; jSuppress >= 0; jSuppress--) {
long position = this.suppressWarningScopePositions[jSuppress];
int startSuppress = (int) (position >>> 32);
int endSuppress = (int) position;
if (start < startSuppress) continue nextSuppress;
if (end > endSuppress) continue nextSuppress;
if (this.suppressWarningIrritants[jSuppress].areAllSet()) break pairLoop; // suppress all?
}
}
int id = options.getIgnoredIrritant(tokenIrritants);
if (id > 0) {
String key = CompilerOptions.optionKeyFromIrritant(id);
this.scope.problemReporter().problemNotAnalysed(value, key);
} else {
this.scope.problemReporter().unusedWarningToken(value);
}
}
}
}
}
break pairLoop;
}
}
}
}
}
}
}
/**
* Bytecode generation
*/
public void generateCode() {
if (this.ignoreFurtherInvestigation) {
if (this.types != null) {
for (int i = 0, count = this.types.length; i < count; i++) {
this.types[i].ignoreFurtherInvestigation = true;
// propagate the flag to request problem type creation
this.types[i].generateCode(this.scope);
}
}
return;
}
try {
if (this.types != null) {
for (int i = 0, count = this.types.length; i < count; i++)
this.types[i].generateCode(this.scope);
}
if (this.moduleDeclaration != null) {
this.moduleDeclaration.generateCode();
}
} catch (AbortCompilationUnit e) {
// ignore
}
}
@Override
public CompilationUnitDeclaration getCompilationUnitDeclaration() {
return this;
}
public char[] getFileName() {
return this.compilationResult.getFileName();
}
public char[] getMainTypeName() {
if (this.compilationResult.compilationUnit == null) {
char[] fileName = this.compilationResult.getFileName();
int start = CharOperation.lastIndexOf('/', fileName) + 1;
if (start == 0 || start < CharOperation.lastIndexOf('\\', fileName))
start = CharOperation.lastIndexOf('\\', fileName) + 1;
int end = CharOperation.lastIndexOf('.', fileName);
if (end == -1)
end = fileName.length;
return CharOperation.subarray(fileName, start, end);
} else {
return this.compilationResult.compilationUnit.getMainTypeName();
}
}
public boolean isEmpty() {
return (this.currentPackage == null) && (this.imports == null) && (this.types == null);
}
public boolean isPackageInfo() {
return CharOperation.equals(getMainTypeName(), TypeConstants.PACKAGE_INFO_NAME);
}
public boolean isModuleInfo() {
return CharOperation.equals(getMainTypeName(), TypeConstants.MODULE_INFO_NAME);
}
public boolean isSuppressed(CategorizedProblem problem) {
if (this.suppressWarningsCount == 0) return false;
int irritant = ProblemReporter.getIrritant(problem.getID());
if (irritant == 0) return false;
int start = problem.getSourceStart();
int end = problem.getSourceEnd();
nextSuppress: for (int iSuppress = 0, suppressCount = this.suppressWarningsCount; iSuppress < suppressCount; iSuppress++) {
long position = this.suppressWarningScopePositions[iSuppress];
int startSuppress = (int) (position >>> 32);
int endSuppress = (int) position;
if (start < startSuppress) continue nextSuppress;
if (end > endSuppress) continue nextSuppress;
if (this.suppressWarningIrritants[iSuppress].isSet(irritant))
return true;
}
return false;
}
public boolean hasFunctionalTypes() {
return this.compilationResult.hasFunctionalTypes;
}
@Override
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
@Override
public StringBuffer print(int indent, StringBuffer output) {
if (this.currentPackage != null) {
//{ObjectTeams: package can have "team" modifier:
/* orig:
printIndent(indent, output).append("package "); //$NON-NLS-1$
:giro */
printIndent(indent, output);
if ((this.currentPackage.modifiers & ExtraCompilerModifiers.AccTeam) != 0)
output.append("team "); //$NON-NLS-1$
output.append("package "); //$NON-NLS-1$
// SH}
this.currentPackage.print(0, output, false).append(";\n"); //$NON-NLS-1$
}
if (this.imports != null)
for (int i = 0; i < this.imports.length; i++) {
printIndent(indent, output).append("import "); //$NON-NLS-1$
ImportReference currentImport = this.imports[i];
if (currentImport.isStatic()) {
output.append("static "); //$NON-NLS-1$
}
//{ObjectTeams:
else if (currentImport.isBase()) {
output.append("base "); //$NON-NLS-1$
}
// SH}
currentImport.print(0, output).append(";\n"); //$NON-NLS-1$
}
if (this.moduleDeclaration != null) {
this.moduleDeclaration.print(indent, output).append("\n"); //$NON-NLS-1$
} else if (this.types != null) {
for (int i = 0; i < this.types.length; i++) {
this.types[i].print(indent, output).append("\n"); //$NON-NLS-1$
}
}
return output;
}
/*
* Force inner local types to update their innerclass emulation
*/
public void propagateInnerEmulationForAllLocalTypes() {
this.isPropagatingInnerClassEmulation = true;
for (int i = 0, max = this.localTypeCount; i < max; i++) {
LocalTypeBinding localType = this.localTypes[i];
// only propagate for reachable local types
if ((localType.scope.referenceType().bits & IsReachable) != 0) {
localType.updateInnerEmulationDependents();
}
}
}
public void recordStringLiteral(StringLiteral literal, boolean fromRecovery) {
if (this.stringLiteralsStart != null) {
if (this.stringLiteralsStart.contains(literal.sourceStart)) return;
this.stringLiteralsStart.add(literal.sourceStart);
} else if (fromRecovery) {
this.stringLiteralsStart = new HashSetOfInt(this.stringLiteralsPtr + STRING_LITERALS_INCREMENT);
for (int i = 0; i < this.stringLiteralsPtr; i++) {
this.stringLiteralsStart.add(this.stringLiterals[i].sourceStart);
}
if (this.stringLiteralsStart.contains(literal.sourceStart)) return;
this.stringLiteralsStart.add(literal.sourceStart);
}
if (this.stringLiterals == null) {
this.stringLiterals = new StringLiteral[STRING_LITERALS_INCREMENT];
this.stringLiteralsPtr = 0;
} else {
int stackLength = this.stringLiterals.length;
if (this.stringLiteralsPtr == stackLength) {
System.arraycopy(
this.stringLiterals,
0,
this.stringLiterals = new StringLiteral[stackLength + STRING_LITERALS_INCREMENT],
0,
stackLength);
}
}
this.stringLiterals[this.stringLiteralsPtr++] = literal;
}
private boolean isLambdaExpressionCopyContext(ReferenceContext context) {
if (context instanceof LambdaExpression && context != ((LambdaExpression) context).original())
return true; // Do not record from copies. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=441929
Scope cScope = context instanceof AbstractMethodDeclaration ? ((AbstractMethodDeclaration) context).scope :
context instanceof TypeDeclaration ? ((TypeDeclaration) context).scope :
context instanceof LambdaExpression ? ((LambdaExpression) context).scope :
null;
return cScope != null ? isLambdaExpressionCopyContext(cScope.parent.referenceContext()) : false;
}
//{ObjectTeams: decapsulation warning needs to check before entering warning
/**
* @param problemID the problem to check
* @param start
* @param end
* @param foundIrritants if a suppress warning has been used record that fact here, if recording is not desired null may be passed.
*/
public boolean isWarningSuppressedAt(int problemID, int start, int end, IrritantSet[] foundIrritants) {
for (int j = 0, max = this.suppressWarningsCount; j < max; j++) {
long position = this.suppressWarningScopePositions[j];
int startSuppress = (int) (position >>> 32);
int endSuppress = (int) position;
if (start < startSuppress) continue;
if (end > endSuppress) continue;
int irritant = ProblemReporter.getIrritant(problemID);
// record the fact that this suppress has been used:
if (this.suppressWarningIrritants[j].isSet(irritant)) {
if (foundIrritants != null) {
if (foundIrritants[j] == null)
foundIrritants[j] = new IrritantSet(irritant);
else
foundIrritants[j].set(irritant);
}
return true;
}
}
return false;
}
// SH}
public void recordSuppressWarnings(IrritantSet irritants, Annotation annotation, int scopeStart, int scopeEnd, ReferenceContext context) {
if (isLambdaExpressionCopyContext(context))
return; // Do not record from copies. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=441929
if (this.suppressWarningIrritants == null) {
this.suppressWarningIrritants = new IrritantSet[3];
this.suppressWarningAnnotations = new Annotation[3];
this.suppressWarningScopePositions = new long[3];
} else if (this.suppressWarningIrritants.length == this.suppressWarningsCount) {
System.arraycopy(this.suppressWarningIrritants, 0,this.suppressWarningIrritants = new IrritantSet[2*this.suppressWarningsCount], 0, this.suppressWarningsCount);
System.arraycopy(this.suppressWarningAnnotations, 0,this.suppressWarningAnnotations = new Annotation[2*this.suppressWarningsCount], 0, this.suppressWarningsCount);
System.arraycopy(this.suppressWarningScopePositions, 0,this.suppressWarningScopePositions = new long[2*this.suppressWarningsCount], 0, this.suppressWarningsCount);
}
final long scopePositions = ((long)scopeStart<<32) + scopeEnd;
for (int i = 0, max = this.suppressWarningsCount; i < max; i++) {
if (this.suppressWarningAnnotations[i] == annotation
&& this.suppressWarningScopePositions[i] == scopePositions
&& this.suppressWarningIrritants[i].hasSameIrritants(irritants)) {
// annotation data already recorded
return;
}
}
this.suppressWarningIrritants[this.suppressWarningsCount] = irritants;
this.suppressWarningAnnotations[this.suppressWarningsCount] = annotation;
this.suppressWarningScopePositions[this.suppressWarningsCount++] = scopePositions;
}
/*
* Keep track of all local types, so as to update their innerclass
* emulation later on.
*/
public void record(LocalTypeBinding localType) {
if (this.localTypeCount == 0) {
this.localTypes = new LocalTypeBinding[5];
} else if (this.localTypeCount == this.localTypes.length) {
System.arraycopy(this.localTypes, 0, (this.localTypes = new LocalTypeBinding[this.localTypeCount * 2]), 0, this.localTypeCount);
}
this.localTypes[this.localTypeCount++] = localType;
}
/*
* Keep track of all lambda/method reference expressions, so as to be able to look it up later without
* having to traverse AST. Return the "ordinal" returned by the enclosing type.
*/
public int record(FunctionalExpression expression) {
if (this.functionalExpressionsCount == 0) {
this.functionalExpressions = new FunctionalExpression[5];
} else if (this.functionalExpressionsCount == this.functionalExpressions.length) {
System.arraycopy(this.functionalExpressions, 0, (this.functionalExpressions = new FunctionalExpression[this.functionalExpressionsCount * 2]), 0, this.functionalExpressionsCount);
}
this.functionalExpressions[this.functionalExpressionsCount++] = expression;
return expression.enclosingScope.classScope().referenceContext.record(expression);
}
//{ObjectTeams: should only be called by Dependencies!
// SH}
public void resolve() {
int startingTypeIndex = 0;
boolean isPackageInfo = isPackageInfo();
if (this.types != null && isPackageInfo) {
// resolve synthetic type declaration
final TypeDeclaration syntheticTypeDeclaration = this.types[0];
// set empty javadoc to avoid missing warning (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95286)
if (syntheticTypeDeclaration.javadoc == null) {
syntheticTypeDeclaration.javadoc = new Javadoc(syntheticTypeDeclaration.declarationSourceStart, syntheticTypeDeclaration.declarationSourceStart);
}
syntheticTypeDeclaration.resolve(this.scope);
/*
* resolve javadoc package if any, skip this step if we don't have a valid scope due to an earlier error (bug 252555)
* we do it now as the javadoc in the fake type won't be resolved. The peculiar usage of MethodScope to resolve the
* package level javadoc is because the CU level resolve method is a NOP to mimic Javadoc's behavior and can't be used
* as such.
*/
if (this.javadoc != null && syntheticTypeDeclaration.staticInitializerScope != null) {
this.javadoc.resolve(syntheticTypeDeclaration.staticInitializerScope);
}
startingTypeIndex = 1;
} else {
// resolve compilation unit javadoc package if any
if (this.javadoc != null) {
this.javadoc.resolve(this.scope);
}
}
if (this.currentPackage != null && this.currentPackage.annotations != null && !isPackageInfo) {
this.scope.problemReporter().invalidFileNameForPackageAnnotations(this.currentPackage.annotations[0]);
}
try {
if (this.types != null) {
for (int i = startingTypeIndex, count = this.types.length; i < count; i++) {
this.types[i].resolve(this.scope);
}
}
//{ObjectTeams: postponed to analyseCode():
/* orig:
if (!this.compilationResult.hasMandatoryErrors()) checkUnusedImports();
:giro */
if (!isRoleUnit())
// SH}
reportNLSProblems();
} catch (AbortCompilationUnit e) {
this.ignoreFurtherInvestigation = true;
return;
}
}
//{ObjectTeams: accessible for TypeDeclaration.resolve():
/* orig:
private void reportNLSProblems() {
:giro */
void reportNLSProblems() {
// SH}
if (this.nlsTags != null || this.stringLiterals != null) {
final int stringLiteralsLength = this.stringLiteralsPtr;
final int nlsTagsLength = this.nlsTags == null ? 0 : this.nlsTags.length;
if (stringLiteralsLength == 0) {
if (nlsTagsLength != 0) {
for (int i = 0; i < nlsTagsLength; i++) {
NLSTag tag = this.nlsTags[i];
if (tag != null) {
this.scope.problemReporter().unnecessaryNLSTags(tag.start, tag.end);
}
}
}
} else if (nlsTagsLength == 0) {
// resize string literals
if (this.stringLiterals.length != stringLiteralsLength) {
System.arraycopy(this.stringLiterals, 0, (this.stringLiterals = new StringLiteral[stringLiteralsLength]), 0, stringLiteralsLength);
}
Arrays.sort(this.stringLiterals, STRING_LITERAL_COMPARATOR);
for (int i = 0; i < stringLiteralsLength; i++) {
this.scope.problemReporter().nonExternalizedStringLiteral(this.stringLiterals[i]);
}
} else {
// need to iterate both arrays to find non matching elements
if (this.stringLiterals.length != stringLiteralsLength) {
System.arraycopy(this.stringLiterals, 0, (this.stringLiterals = new StringLiteral[stringLiteralsLength]), 0, stringLiteralsLength);
}
Arrays.sort(this.stringLiterals, STRING_LITERAL_COMPARATOR);
int indexInLine = 1;
int lastLineNumber = -1;
StringLiteral literal = null;
int index = 0;
int i = 0;
stringLiteralsLoop: for (; i < stringLiteralsLength; i++) {
literal = this.stringLiterals[i];
final int literalLineNumber = literal.lineNumber;
if (lastLineNumber != literalLineNumber) {
indexInLine = 1;
lastLineNumber = literalLineNumber;
} else {
indexInLine++;
}
if (index < nlsTagsLength) {
nlsTagsLoop: for (; index < nlsTagsLength; index++) {
NLSTag tag = this.nlsTags[index];
if (tag == null) continue nlsTagsLoop;
int tagLineNumber = tag.lineNumber;
if (literalLineNumber < tagLineNumber) {
this.scope.problemReporter().nonExternalizedStringLiteral(literal);
continue stringLiteralsLoop;
} else if (literalLineNumber == tagLineNumber) {
if (tag.index == indexInLine) {
this.nlsTags[index] = null;
index++;
continue stringLiteralsLoop;
} else {
nlsTagsLoop2: for (int index2 = index + 1; index2 < nlsTagsLength; index2++) {
NLSTag tag2 = this.nlsTags[index2];
if (tag2 == null) continue nlsTagsLoop2;
int tagLineNumber2 = tag2.lineNumber;
if (literalLineNumber == tagLineNumber2) {
if (tag2.index == indexInLine) {
this.nlsTags[index2] = null;
continue stringLiteralsLoop;
} else {
continue nlsTagsLoop2;
}
} else {
this.scope.problemReporter().nonExternalizedStringLiteral(literal);
continue stringLiteralsLoop;
}
}
this.scope.problemReporter().nonExternalizedStringLiteral(literal);
continue stringLiteralsLoop;
}
} else {
this.scope.problemReporter().unnecessaryNLSTags(tag.start, tag.end);
continue nlsTagsLoop;
}
}
}
// all nls tags have been processed, so remaining string literals are not externalized
break stringLiteralsLoop;
}
for (; i < stringLiteralsLength; i++) {
this.scope.problemReporter().nonExternalizedStringLiteral(this.stringLiterals[i]);
}
if (index < nlsTagsLength) {
for (; index < nlsTagsLength; index++) {
NLSTag tag = this.nlsTags[index];
if (tag != null) {
this.scope.problemReporter().unnecessaryNLSTags(tag.start, tag.end);
}
}
}
}
}
}
@Override
public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
}
@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}
//{ObjectTeams: and let us remove it again:
@Override
public void resetErrorFlag() {
this.ignoreFurtherInvestigation = false;
}
// SH}
public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope) {
traverse(visitor, unitScope, true);
}
public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope, boolean skipOnError) {
if (skipOnError && this.ignoreFurtherInvestigation)
return;
try {
if (visitor.visit(this, this.scope)) {
if (this.types != null && isPackageInfo()) {
// resolve synthetic type declaration
final TypeDeclaration syntheticTypeDeclaration = this.types[0];
// resolve javadoc package if any
final MethodScope methodScope = syntheticTypeDeclaration.staticInitializerScope;
// Don't traverse in null scope and invite trouble a la bug 252555.
if (this.javadoc != null && methodScope != null) {
this.javadoc.traverse(visitor, methodScope);
}
// Don't traverse in null scope and invite trouble a la bug 252555.
if (this.currentPackage != null && methodScope != null) {
final Annotation[] annotations = this.currentPackage.annotations;
if (annotations != null) {
int annotationsLength = annotations.length;
for (int i = 0; i < annotationsLength; i++) {
annotations[i].traverse(visitor, methodScope);
}
}
}
}
if (this.currentPackage != null) {
this.currentPackage.traverse(visitor, this.scope);
}
if (this.imports != null) {
int importLength = this.imports.length;
for (int i = 0; i < importLength; i++) {
this.imports[i].traverse(visitor, this.scope);
}
}
if (this.types != null) {
int typesLength = this.types.length;
for (int i = 0; i < typesLength; i++) {
this.types[i].traverse(visitor, this.scope);
}
}
if (this.isModuleInfo() && this.moduleDeclaration != null) {
this.moduleDeclaration.traverse(visitor, this.scope);
}
}
visitor.endVisit(this, this.scope);
} catch (AbortCompilationUnit e) {
// ignore
}
}
//{ObjectTeams
/**
* Does the CUD represent a role file?
*/
public boolean isRoleUnit() {
if (this.types != null && this.types.length > 0) {
TypeDeclaration t1 = this.types[0];
return (t1.modifiers & ExtraCompilerModifiers.AccRole) != 0;
}
return false;
}
// SH}
public ModuleBinding module(LookupEnvironment environment) {
if (this.moduleDeclaration != null) {
ModuleBinding binding = this.moduleDeclaration.binding;
if (binding != null)
return binding;
}
if (this.compilationResult != null) {
ICompilationUnit compilationUnit = this.compilationResult.compilationUnit;
if (compilationUnit != null)
return compilationUnit.module(environment);
}
return environment.module;
}
}