| /******************************************************************************* |
| * 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 |
| *******************************************************************************/ |
| 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) { |
| for (int iProblem = 0, length = problemCount; iProblem < length; iProblem++) { |
| if (problems[iProblem] instanceof DefaultProblem) { |
| ((DefaultProblem)problems[iProblem]).reportError(); |
| } |
| } |
| 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; |
| } |
| } |