Copyright (c) 2000, 2019 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 - 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. Red Hat Inc. - add module-info Javadoc support
/******************************************************************************* * Copyright (c) 2000, 2019 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 - 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. * Red Hat Inc. - add module-info Javadoc support *******************************************************************************/
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.core.compiler.IProblem; 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.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.CompilationUnitScope; 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.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; @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; 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() { 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() { 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.cleanUp(); // 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]; // 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++){ 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.cleanUp(); } } 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() { int problemCount = this.compilationResult.problemCount; CategorizedProblem[] problems = this.compilationResult.problems; if (this.suppressWarningsCount == 0) { return; } int removed = 0; IrritantSet[] foundIrritants = new IrritantSet[this.suppressWarningsCount]; 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; } } // 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 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
/** * 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) { printIndent(indent, output).append("package "); //$NON-NLS-1$ 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$ } 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; } 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); } public void resolve() { int startingTypeIndex = 0; boolean isPackageInfo = isPackageInfo(); boolean isModuleInfo = isModuleInfo(); 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 if (this.moduleDeclaration != null && isModuleInfo) { if (this.javadoc != null) { this.javadoc.resolve((MethodScope)this.moduleDeclaration.scope); } else if (this.moduleDeclaration.binding != null) { ProblemReporter reporter = this.scope.problemReporter(); int severity = reporter.computeSeverity(IProblem.JavadocMissing); if (severity != ProblemSeverities.Ignore) { reporter.javadocModuleMissing(this.moduleDeclaration.declarationSourceStart, this.moduleDeclaration.bodyStart, severity); } } } 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); } } if (!this.compilationResult.hasMandatoryErrors()) checkUnusedImports(); reportNLSProblems(); } catch (AbortCompilationUnit e) { this.ignoreFurtherInvestigation = true; return; } } private void reportNLSProblems() { 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; } 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 } } 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; } }