diff options
author | Jeff Johnston | 2019-08-07 18:04:31 +0000 |
---|---|---|
committer | Stephan Herrmann | 2019-11-15 22:14:47 +0000 |
commit | 02bfee1383373427dca07e6f5e902c6abb9952f1 (patch) | |
tree | 166794e54367b3e68566ba02bda022f34ca32470 | |
parent | c90af8793f3e49e6987b7f29394594bc1d7e3892 (diff) | |
download | eclipse.jdt.core-02bfee1383373427dca07e6f5e902c6abb9952f1.tar.gz eclipse.jdt.core-02bfee1383373427dca07e6f5e902c6abb9952f1.tar.xz eclipse.jdt.core-02bfee1383373427dca07e6f5e902c6abb9952f1.zip |
Bug 549855 - [javadoc] Add error checking for @uses and @provides tagsI20191119-0850I20191119-0725I20191119-0510I20191119-0315I20191119-0020I20191118-2230I20191118-1800I20191118-0600I20191117-1800I20191117-0600I20191116-1800I20191116-0600I20191115-1800
- change AbstractCommentParser.getTokenEndPosition() to be protected
- add module-info support to CompilationUnitDeclaration.resolve()
- add new IProblem values for @provides and @uses errors
- add new code to Javadoc resolve(Methodscope) method to support
parsing module-info javadoc
- add resolveUsesTags() and resolveProvidesTags() methods to Javadoc to
check tags against actual uses and provides statements
- add parseUsesReference() and parseProvidesReference() methods to
JavadocParser to parse @uses and @provides tags
- add new IProblem messages to messages.properties
- add javadoc field to ModuleDeclaration
- add new uses and provides tag error support to ProblemReporter
- in Parser.consumeModuleDeclaration() set up compilationUnit javadoc
- add new javadocModuleMissing method to ProblemReporter
- add new JavadocTestForModule test class
- add new IJavadocTypeReference interface and have
JavaQualifiedTypeReference and JavaSingleTypeReference implement it
Change-Id: Ib4eff519fe53c1fdb9ceca5d480dccb55aa23ab8
14 files changed, 1478 insertions, 21 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index ce9c925041..252ac420c8 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -702,9 +702,11 @@ public void test011_problem_categories() { expectedProblemAttributes.put("JavadocAmbiguousMethodReference", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocAmbiguousType", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateParamName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocDuplicateProvidesTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateReturnTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateThrowsClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocDuplicateUsesTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocEmptyReturnTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocGenericConstructorTypeArgumentMismatch", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocGenericMethodTypeArgumentMismatch", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); @@ -719,6 +721,8 @@ public void test011_problem_categories() { expectedProblemAttributes.put("JavadocInvalidParamName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidParamTagName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidParamTagTypeParameter", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidProvidesClass", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidProvidesClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidSeeArgs", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidSeeHref", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidSeeReference", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); @@ -726,6 +730,8 @@ public void test011_problem_categories() { expectedProblemAttributes.put("JavadocInvalidTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidThrowsClass", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidThrowsClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidUsesClass", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidUsesClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidValueReference", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMalformedSeeReference", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMessagePrefix", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); @@ -734,11 +740,15 @@ public void test011_problem_categories() { expectedProblemAttributes.put("JavadocMissingIdentifier", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingParamName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingParamTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingProvidesClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingProvidesTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingReturnTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingSeeReference", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingTagDescription", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingThrowsClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocMissingThrowsTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingUsesClassName", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingUsesTag", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocNoMessageSendOnArrayType", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocNoMessageSendOnBaseType", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); expectedProblemAttributes.put("JavadocNonGenericConstructor", new ProblemAttributes(CategorizedProblem.CAT_JAVADOC)); @@ -1674,9 +1684,11 @@ public void test012_compiler_problems_tuning() { expectedProblemAttributes.put("JavadocAmbiguousMethodReference", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocAmbiguousType", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateParamName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocDuplicateProvidesTag", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateReturnTag", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateTag", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocDuplicateThrowsClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocDuplicateUsesTag", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocEmptyReturnTag", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocGenericConstructorTypeArgumentMismatch", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocGenericMethodTypeArgumentMismatch", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); @@ -1691,6 +1703,8 @@ public void test012_compiler_problems_tuning() { expectedProblemAttributes.put("JavadocInvalidParamName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidParamTagName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidParamTagTypeParameter", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidProvidesClass", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidProvidesClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidSeeArgs", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidSeeHref", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidSeeReference", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); @@ -1698,6 +1712,8 @@ public void test012_compiler_problems_tuning() { expectedProblemAttributes.put("JavadocInvalidTag", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidThrowsClass", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidThrowsClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidUsesClass", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocInvalidUsesClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocInvalidValueReference", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMalformedSeeReference", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMessagePrefix", SKIP); @@ -1706,11 +1722,17 @@ public void test012_compiler_problems_tuning() { expectedProblemAttributes.put("JavadocMissingIdentifier", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMissingParamName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMissingParamTag", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS)); + expectedProblemAttributes.put("JavadocMissingProvidesClass", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingProvidesClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingProvidesTag", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS)); expectedProblemAttributes.put("JavadocMissingReturnTag", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS)); expectedProblemAttributes.put("JavadocMissingSeeReference", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMissingTagDescription", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMissingThrowsClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocMissingThrowsTag", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS)); + expectedProblemAttributes.put("JavadocMissingUsesClass", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingUsesClassName", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); + expectedProblemAttributes.put("JavadocMissingUsesTag", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS)); expectedProblemAttributes.put("JavadocNoMessageSendOnArrayType", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocNoMessageSendOnBaseType", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); expectedProblemAttributes.put("JavadocNonGenericConstructor", new ProblemAttributes(JavaCore.COMPILER_PB_INVALID_JAVADOC)); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTestForModule.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTestForModule.java new file mode 100644 index 0000000000..00ecdbcc13 --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTestForModule.java @@ -0,0 +1,961 @@ +/******************************************************************************* + * Copyright (c) 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 + * Red Hat Inc. - copied from ModuleCompilationTests and used for Javadoc + *******************************************************************************/ +package org.eclipse.jdt.core.tests.compiler.regression; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.core.tests.util.Util; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +public class JavadocTestForModule extends AbstractBatchCompilerTest { + + static { +// TESTS_NAMES = new String[] { "testBug549855a" }; + // TESTS_NUMBERS = new int[] { 1 }; + // TESTS_RANGE = new int[] { 298, -1 }; + } + + public JavadocTestForModule(String name) { + super(name); + } + + public static Test suite() { + return buildMinimalComplianceTestSuite(testClass(), F_9); + } + + public static Class<?> testClass() { + return JavadocTestForModule.class; + } + + protected void writeFileCollecting(List<String> collectedFiles, String directoryName, String fileName, String source) { + writeFile(directoryName, fileName, source); + collectedFiles.add(directoryName+File.separator+fileName); + } + + protected void writeFile(String directoryName, String fileName, String source) { + File directory = new File(directoryName); + if (!directory.exists()) { + if (!directory.mkdirs()) { + System.out.println("Could not create " + directoryName); + return; + } + } + String filePath = directory.getAbsolutePath() + File.separator + fileName; + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(filePath)); + writer.write(source); + writer.flush(); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + + class Runner extends AbstractRegressionTest.Runner { + StringBuffer commandLine = new StringBuffer(); + String outputDir = OUTPUT_DIR + File.separator + "javac"; + List<String> fileNames = new ArrayList<>(); + /** will replace any -8, -9 ... option for javac */ + String javacVersionOptions; + + Runner() { + this.javacTestOptions = JavacTestOptions.DEFAULT; + this.expectedOutputString = ""; + this.expectedErrorString = ""; + } + /** Create a source file and add the filename to the compiler command line. */ + void createFile(String directoryName, String fileName, String source) { + writeFileCollecting(this.fileNames, directoryName, fileName, source); + } + Set<String> runConformModuleTest() { + if (!this.fileNames.isEmpty()) { + this.shouldFlushOutputDirectory = false; + if (this.testFiles == null) + this.testFiles = new String[0]; + for (String fileName : this.fileNames) { + this.commandLine.append(" \"").append(fileName).append("\""); + } + } + String commandLineString = this.commandLine.toString(); + String javacCommandLine = adjustForJavac(commandLineString, this.javacVersionOptions); + return JavadocTestForModule.this.runConformModuleTest(this.testFiles, commandLineString, + this.expectedOutputString, this.expectedErrorString, + this.shouldFlushOutputDirectory, this.outputDir, + this.javacTestOptions, javacCommandLine); + } + } + + void runConformModuleTest(List<String> testFileNames, StringBuffer commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory) + { + runConformModuleTest(testFileNames, commandLine, + expectedFailureOutOutputString, expectedFailureErrOutputString, shouldFlushOutputDirectory, OUTPUT_DIR + File.separator + "javac"); + } + + void runConformModuleTest(List<String> testFileNames, StringBuffer commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String output) + { + for (String file : testFileNames) + commandLine.append(" \"").append(file).append("\""); + runConformModuleTest(new String[0], commandLine.toString(), + expectedFailureOutOutputString, expectedFailureErrOutputString, shouldFlushOutputDirectory, + output, JavacTestOptions.DEFAULT, null); + } + + Set<String> runConformModuleTest(String[] testFiles, String commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory) + { + return runConformModuleTest(testFiles, commandLine, expectedFailureErrOutputString, expectedFailureErrOutputString, + shouldFlushOutputDirectory, OUTPUT_DIR, JavacTestOptions.DEFAULT, null); + } + + Set<String> runConformModuleTest(String[] testFiles, String commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String output, JavacTestOptions options, String javacCommandLine) + { + this.runConformTest(testFiles, commandLine, expectedFailureOutOutputString, expectedFailureErrOutputString, shouldFlushOutputDirectory); + if (RUN_JAVAC) { + File outputDir = new File(output); + final Set<String> outFiles = new HashSet<>(); + walkOutFiles(output, outFiles, true); + String[] testFileNames = new String[testFiles.length/2]; + for (int i = 0; i < testFileNames.length; i++) { + testFileNames[i] = testFiles[i*2]; + } + if (javacCommandLine == null) { + javacCommandLine = adjustForJavac(commandLine, null); + } + for (JavacCompiler javacCompiler : javacCompilers) { + if (javacCompiler.compliance < ClassFileConstants.JDK9) + continue; + if (options.skip(javacCompiler)) { + System.err.println("Skip testing javac in "+testName()); + continue; + } + StringBuffer log = new StringBuffer(); + try { + long compileResult = javacCompiler.compile( + outputDir, /* directory */ + javacCommandLine /* options */, + testFileNames /* source file names */, + log, + false); // don't repeat filenames on the command line + if (compileResult != 0) { + System.err.println("Previous error was from "+testName()); + fail("Unexpected error from javac"); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + throw new AssertionFailedError(e.getMessage()); + } + final Set<String> expectedFiles = new HashSet<>(outFiles); + walkOutFiles(output, expectedFiles, false); + for (String missingFile : expectedFiles) + System.err.println("Missing output file from javac: "+missingFile); + } + return outFiles; + } + return null; + } + + void runNegativeModuleTest(List<String> testFileNames, StringBuffer commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String javacErrorMatch) { + runNegativeModuleTest(testFileNames, commandLine, expectedFailureOutOutputString, + expectedFailureErrOutputString, shouldFlushOutputDirectory, javacErrorMatch, OUTPUT_DIR + File.separator + "javac"); + } + + void runNegativeModuleTest(List<String> testFileNames, StringBuffer commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String javacErrorMatch, String output) + { + runNegativeModuleTest(testFileNames, commandLine, expectedFailureOutOutputString, expectedFailureErrOutputString, + shouldFlushOutputDirectory, javacErrorMatch, output, JavacTestOptions.DEFAULT); + } + void runNegativeModuleTest(List<String> testFileNames, StringBuffer commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String javacErrorMatch, String output, JavacTestOptions options) + { + for (String file : testFileNames) + commandLine.append(" \"").append(file).append("\""); + runNegativeModuleTest(new String[0], commandLine.toString(), + expectedFailureOutOutputString, expectedFailureErrOutputString, shouldFlushOutputDirectory, javacErrorMatch, output, + options); + } + void runNegativeModuleTest(String[] testFiles, String commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String javacErrorMatch) { + runNegativeModuleTest(testFiles, commandLine, expectedFailureOutOutputString, expectedFailureErrOutputString, + shouldFlushOutputDirectory, javacErrorMatch, OUTPUT_DIR, JavacTestOptions.DEFAULT); + } + + void runNegativeModuleTest(String[] testFiles, String commandLine, + String expectedFailureOutOutputString, String expectedFailureErrOutputString, + boolean shouldFlushOutputDirectory, String javacErrorMatch, String output, JavacTestOptions options) + { + this.runNegativeTest(testFiles, commandLine, expectedFailureOutOutputString, expectedFailureErrOutputString, shouldFlushOutputDirectory); + if (RUN_JAVAC) { + String[] testFileNames = new String[testFiles.length/2]; + for (int i = 0; i < testFileNames.length; i++) { + testFileNames[i] = testFiles[i*2]; + } + File outputDir = new File(OUTPUT_DIR); + final Set<String> outFiles = new HashSet<>(); + walkOutFiles(output, outFiles, true); + for (JavacCompiler javacCompiler : javacCompilers) { + if (javacCompiler.compliance < ClassFileConstants.JDK9) + continue; + JavacTestOptions.Excuse excuse = options.excuseFor(javacCompiler); + + commandLine = adjustForJavac(commandLine, null); + StringBuffer log = new StringBuffer(); + int mismatch = 0; + try { + long compileResult = javacCompiler.compile( + outputDir, /* directory */ + commandLine /* options */, + testFileNames /* source file names */, + log); + if (compileResult == 0) { + mismatch = JavacTestOptions.MismatchType.EclipseErrorsJavacNone; + javacErrorMatch = expectedFailureErrOutputString; + System.err.println("Previous error was from "+testName()); + } else if (!log.toString().contains(javacErrorMatch)) { + mismatch = JavacTestOptions.MismatchType.CompileErrorMismatch; + System.err.println(testName()+": Error match " + javacErrorMatch + " not found in \n"+log.toString()); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + throw new AssertionFailedError(e.getMessage()); + } + handleMismatch(javacCompiler, testName(), testFiles, javacErrorMatch, + "", "", log, "", "", + excuse, mismatch); + final Set<String> expectedFiles = new HashSet<>(outFiles); + walkOutFiles(output, expectedFiles, false); + for (String missingFile : expectedFiles) + System.err.println("Missing output file from javac: "+missingFile); + } + } + } + + /** + * @param commandLine command line arguments as used for ecj + * @param versionOptions if non-null use this to replace any ecj-specific -8, -9 etc. arg. + * If ecj-specific arg is not found, append anyway + * @return commandLine adjusted for javac + */ + String adjustForJavac(String commandLine, String versionOptions) { + String[] tokens = commandLine.split(" "); + StringBuffer buf = new StringBuffer(); + boolean skipNext = false; + for (int i = 0; i < tokens.length; i++) { + if (skipNext) { + skipNext = false; + continue; + } + if (tokens[i].trim().equals("-9")) { + if (versionOptions == null) + buf.append(' ').append(" --release 9 "); + continue; + } + if (tokens[i].trim().equals("-8")) { + if (versionOptions == null) + buf.append(' ').append(" --release 8 "); + continue; + } + if (tokens[i].startsWith("-warn") || tokens[i].startsWith("-err") || tokens[i].startsWith("-info")) { + if (tokens[i].contains("exports") && !tokens[i].contains("-exports")) + buf.append(" -Xlint:exports "); + continue; + } + if (tokens[i].trim().equals("-classNames")) { + skipNext = true; + continue; + } + buf.append(tokens[i]).append(' '); + } + if (versionOptions != null) { + buf.append(versionOptions); + } + return buf.toString(); + } + + private void walkOutFiles(final String outputLocation, final Set<String> fileNames, boolean add) { + if (!(new File(outputLocation)).exists()) + return; + try { + Files.walkFileTree(FileSystems.getDefault().getPath(outputLocation), new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.toString().endsWith(".class")) { + if (add) { + fileNames.add(file.toString()); + } else { + if (!fileNames.remove(file.toString())) + System.err.println("Unexpected output file from javac: "+file.toString()); + } + Files.delete(file); + } + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + if (!dir.toString().equals(outputLocation)) { + try { + Files.delete(dir); + } catch (DirectoryNotEmptyException ex) { + // expected + } + } + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + e.printStackTrace(); + throw new AssertionFailedError(e.getMessage()); + } + } + + public void testBug549855a() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 5)\n" + + " provides p.I1 with p.P1;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing provides tag\n" + + "----------\n" + + "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 6)\n" + + " uses java.util.Currency;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing uses tag\n" + + "----------\n" + + "2 problems (2 errors)\n", + false, + "missing tags"); + } + + public void testBug549855b() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " @provides p.I\n" + + " @uses java.util.Currenc\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 2)\n" + + " @provides p.I\n" + + " ^^^\n" + + "Javadoc: Invalid provides class\n" + + "----------\n" + + "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 3)\n" + + " @uses java.util.Currenc\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Invalid uses class\n" + + "----------\n" + + "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 7)\n" + + " provides p.I1 with p.P1;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing provides tag\n" + + "----------\n" + + "4. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 8)\n" + + " uses java.util.Currency;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing uses tag\n" + + "----------\n" + + "4 problems (4 errors)\n", + false, + "missing and invalid tags"); + } + + public void testBug549855c() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " @provides p.I1\n" + + " @uses java.util.Currency\n" + + " @provides p.I1\n" + + " @uses java.util.Currency\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 4)\n" + + " @provides p.I1\n" + + " ^^^^\n" + + "Javadoc: Duplicate provides tag\n" + + "----------\n" + + "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 5)\n" + + " @uses java.util.Currency\n" + + " ^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Duplicate uses tag\n" + + "----------\n" + + "2 problems (2 errors)\n", + false, + "duplicate tags"); + } + + public void testBug549855d() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " @provides p.I1\n" + + " @uses java.util.Currency\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuffer buffer = new StringBuffer(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" "); + + runConformModuleTest(files, buffer, "", "", false); + } + + public void testBug549855e() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " @provides p.I1\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 7)\n" + + " uses java.util.Currency;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing uses tag\n" + + "----------\n" + + "1 problem (1 error)\n", + false, + "missing tags"); + } + + public void testBug549855f() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " @uses java.util.Currency\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 6)\n" + + " provides p.I1 with p.P1;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing provides tag\n" + + "----------\n" + + "1 problem (1 error)\n", + false, + "missing tags"); + } + + public void testBug549855g() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 5)\n" + + " provides p.I1 with p.P1;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing provides tag\n" + + "----------\n" + + "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 6)\n" + + " uses java.util.Currency;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing uses tag\n" + + "----------\n" + + "2 problems (2 errors)\n", + false, + "missing tags"); + } + + public void testBug549855h() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "/**\n" + + " * @provides p.I\n" + + " * @uses java.util.Currenc\n" + + " */\n" + + "module mod.one { \n" + + " exports p;\n" + + " provides p.I1 with p.P1;\n" + + " uses java.util.Currency;\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "I1.java", + "package p;\n" + + "/**\n" + + " * interface I1\n" + + " */\n" + + "public interface I1 {\n" + + " /**\n" + + " * Method foo\n" + + " * @return int\n" + + " */\n" + + " public int foo();\n" + + "}"); + writeFileCollecting(files, moduleLoc + File.separator + "p", "P1.java", + "package p;\n" + + "/**\n" + + " * class P1\n" + + " */\n" + + "public class P1 implements I1 {\n" + + " @Override\n" + + " public int foo() { return 0; }\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "I1.java ") + .append(moduleLoc + File.separator + "p" + File.separator + "P1.java"); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 2)\n" + + " * @provides p.I\n" + + " ^^^\n" + + "Javadoc: Invalid provides class\n" + + "----------\n" + + "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 3)\n" + + " * @uses java.util.Currenc\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Invalid uses class\n" + + "----------\n" + + "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 7)\n" + + " provides p.I1 with p.P1;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing provides tag\n" + + "----------\n" + + "4. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 8)\n" + + " uses java.util.Currency;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing uses tag\n" + + "----------\n" + + "4 problems (4 errors)\n", + false, + "invalid tags"); + } + + public void testBug549855i() { + File outputDirectory = new File(OUTPUT_DIR); + Util.flushDirectoryContent(outputDirectory); + String out = "bin"; + String directory = OUTPUT_DIR + File.separator + "src"; + String moduleLoc = directory + File.separator + "mod.one"; + List<String> files = new ArrayList<>(); + writeFileCollecting(files, moduleLoc, "module-info.java", + "module mod.one {\n" + + "}"); + + StringBuilder buffer = new StringBuilder(); + buffer.append("-d " + OUTPUT_DIR + File.separator + out ) + .append(" -9 ") + .append(" -enableJavadoc ") + .append(" -err:allJavadoc ") + .append(" -classpath \"") + .append(Util.getJavaClassLibsAsString()) + .append("\" ") + .append(" -warn:-unused") + .append(" --module-source-path " + "\"" + directory + "\" ") + .append(moduleLoc + File.separator + "module-info.java "); + + runNegativeModuleTest( + new String[0], + buffer.toString(), + "", + "----------\n" + + "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 1)\n" + + " module mod.one {\n" + + " ^^^^^^^^^^^^^^^\n" + + "Javadoc: Missing comment for module declaration\n" + + "----------\n" + + "1 problem (1 error)\n", + false, + "missing comment"); + } + +} diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java index 64220c7a4d..949332e962 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java @@ -1192,6 +1192,29 @@ void setSourceStart(int sourceStart); int JavadocUnexpectedText = Javadoc + Internal + 518; /** @since 3.1 */ int JavadocInvalidParamTagName = Javadoc + Internal + 519; + /* + * IDs for module errors in Javadoc + */ + /** @since 3.20 */ + int JavadocMissingUsesTag = Javadoc + Internal + 1800; + /** @since 3.20 */ + int JavadocDuplicateUsesTag = Javadoc + Internal + 1801; + /** @since 3.20 */ + int JavadocMissingUsesClassName = Javadoc + Internal + 1802; + /** @since 3.20 */ + int JavadocInvalidUsesClassName = Javadoc + Internal + 1803; + /** @since 3.20 */ + int JavadocInvalidUsesClass = Javadoc + Internal + 1804; + /** @since 3.20 */ + int JavadocMissingProvidesTag = Javadoc + Internal + 1805; + /** @since 3.20 */ + int JavadocDuplicateProvidesTag = Javadoc + Internal + 1806; + /** @since 3.20 */ + int JavadocMissingProvidesClassName = Javadoc + Internal + 1807; + /** @since 3.20 */ + int JavadocInvalidProvidesClassName = Javadoc + Internal + 1808; + /** @since 3.20 */ + int JavadocInvalidProvidesClass = Javadoc + Internal + 1809; /** * Generics diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java index 3e17e1538f..aacb0bb7c3 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java @@ -13,7 +13,8 @@ * 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. + * Frits Jalvingh - contributions for bug 533830. + * Red Hat Inc. - add module-info Javadoc support *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -22,6 +23,7 @@ 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; @@ -345,7 +347,7 @@ public void finalizeProblems() { String key = CompilerOptions.optionKeyFromIrritant(id); this.scope.problemReporter().problemNotAnalysed(inits[iToken], key); } else { - this.scope.problemReporter().unusedWarningToken(inits[iToken]); + this.scope.problemReporter().unusedWarningToken(inits[iToken]); } } } @@ -557,7 +559,7 @@ 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 TypeDeclaration ? ((TypeDeclaration) context).scope : context instanceof LambdaExpression ? ((LambdaExpression) context).scope : null; return cScope != null ? isLambdaExpressionCopyContext(cScope.parent.referenceContext()) : false; @@ -565,7 +567,7 @@ private boolean isLambdaExpressionCopyContext(ReferenceContext context) { 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]; @@ -603,7 +605,7 @@ public void record(LocalTypeBinding localType) { } /* - * Keep track of all lambda/method reference expressions, so as to be able to look it up later without + * 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) { @@ -619,6 +621,7 @@ public int record(FunctionalExpression 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]; @@ -637,6 +640,17 @@ public void resolve() { 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) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IJavadocTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IJavadocTypeReference.java new file mode 100644 index 0000000000..c21cdaec28 --- /dev/null +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IJavadocTypeReference.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2019 Red Hat Inc. 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: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +/** + * Interface to allow Javadoc parser to collect both JavaSingleTypeReference and JavaQualifiedTypeReferences + * + * @author jjohnstn + * + */ +public interface IJavadocTypeReference { + + public int getTagSourceStart(); + public int getTagSourceEnd(); + +} diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java index 308c39c3be..087d33fc8f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corporation and others. + * 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 @@ -33,6 +33,8 @@ public class Javadoc extends ASTNode { public TypeReference[] exceptionReferences; // @throws, @exception public JavadocReturnStatement returnStatement; // @return public Expression[] seeReferences; // @see + public IJavadocTypeReference[] usesReferences; // @uses + public IJavadocTypeReference[] providesReferences; // @provides public long[] inheritedPositions = null; // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600 // Store param references for tag with invalid syntax @@ -262,8 +264,8 @@ public class Javadoc extends ASTNode { return; } // Do nothing - This is to mimic the SDK's javadoc tool behavior, which neither - // sanity checks nor generates documentation using comments at the CU scope - // (unless the unit happens to be package-info.java - in which case we don't come here.) + // sanity checks nor generates documentation using comments at the CU scope + // (unless the unit happens to be package-info.java - in which case we don't come here.) } /* @@ -318,7 +320,7 @@ public class Javadoc extends ASTNode { MethodBinding current = methDecl.binding; // work 'against' better inference in 1.8 (otherwise comparing (G<T> with G<Object>) would fail): if (methScope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8 - && current.typeVariables != Binding.NO_TYPE_VARIABLES) + && current.typeVariables != Binding.NO_TYPE_VARIABLES) { current = current.asRawMethod(methScope.environment()); } @@ -326,7 +328,7 @@ public class Javadoc extends ASTNode { superRef = true; } } - } + } } } } @@ -385,6 +387,11 @@ public class Javadoc extends ASTNode { for (int i = 0; i < length; i++) { this.invalidParameters[i].resolve(methScope, false, false); } + + if (methScope.isModuleScope()) { + resolveUsesTags(methScope, reportMissing); + resolveProvidesTags(methScope, reportMissing); + } } private void resolveReference(Expression reference, Scope scope) { @@ -561,6 +568,148 @@ public class Javadoc extends ASTNode { } /* + * Resolve @uses tags while block scope + */ + private void resolveUsesTags(BlockScope scope, boolean reportMissing) { + ModuleDeclaration moduleDecl = (ModuleDeclaration)scope.referenceContext(); + int usesTagsSize = this.usesReferences == null ? 0 : this.usesReferences.length; + + // If no referenced module then report a problem for each uses tag + if (moduleDecl == null) { + for (int i = 0; i < usesTagsSize; i++) { + IJavadocTypeReference uses = this.usesReferences[i]; + scope.problemReporter().javadocUnexpectedTag(uses.getTagSourceStart(), uses.getTagSourceEnd()); + } + return; + } + + // If no uses tags then report a problem for each uses reference + int usesSize = moduleDecl.usesCount; + if (usesTagsSize == 0) { + if (reportMissing) { + for (int i = 0; i < usesSize; i++) { + UsesStatement uses = moduleDecl.uses[i]; + scope.problemReporter().javadocMissingUsesTag(uses.serviceInterface, uses.sourceStart, uses.sourceEnd, moduleDecl.binding.modifiers); + } + } + } else { + TypeBinding[] bindings = new TypeBinding[usesTagsSize]; + int maxBindings = 0; + + // Scan all @uses tags + for (int i = 0; i < usesTagsSize; i++) { + TypeReference usesRef = (TypeReference)this.usesReferences[i]; + try { + usesRef.resolve(scope); + if (usesRef.resolvedType != null && usesRef.resolvedType.isValidBinding()) { + // Verify duplicated tags + boolean found = false; + for (int j = 0; j < maxBindings && !found; j++) { + if (bindings[j].equals(usesRef.resolvedType)) { + scope.problemReporter().javadocDuplicatedUsesTag(usesRef.sourceStart, usesRef.sourceEnd); + found = true; + } + } + if (!found) { + bindings[maxBindings++] = usesRef.resolvedType; + } + } + } catch (Exception e) { + scope.problemReporter().javadocInvalidUsesClass(usesRef.sourceStart, usesRef.sourceEnd); + } + } + + // Look for undocumented uses + if (reportMissing) { + for (int i = 0; i < usesSize; i++) { + UsesStatement uses = moduleDecl.uses[i]; + boolean found = false; + for (int j = 0; j < maxBindings && !found; j++) { + TypeBinding binding = bindings[j]; + if (uses.serviceInterface.getTypeBinding(scope).equals(binding)) { + found = true; + } + } + if (!found) { + scope.problemReporter().javadocMissingUsesTag(uses.serviceInterface, uses.sourceStart, uses.sourceEnd, moduleDecl.binding.modifiers); + } + } + } + } + } + + /* + * Resolve @provides tags while block scope + */ + private void resolveProvidesTags(BlockScope scope, boolean reportMissing) { + ModuleDeclaration moduleDecl = (ModuleDeclaration)scope.referenceContext(); + int providesTagsSize = this.providesReferences == null ? 0 : this.providesReferences.length; + + // If no referenced module then report a problem for each uses tag + if (moduleDecl == null) { + for (int i = 0; i < providesTagsSize; i++) { + IJavadocTypeReference provides = this.providesReferences[i]; + scope.problemReporter().javadocUnexpectedTag(provides.getTagSourceStart(), provides.getTagSourceEnd()); + } + return; + } + + // If no uses tags then report a problem for each uses reference + int providesSize = moduleDecl.servicesCount; + if (providesTagsSize == 0) { + if (reportMissing) { + for (int i = 0; i < providesSize; i++) { + ProvidesStatement provides = moduleDecl.services[i]; + scope.problemReporter().javadocMissingProvidesTag(provides.serviceInterface, provides.sourceStart, provides.sourceEnd, moduleDecl.binding.modifiers); + } + } + } else { + TypeBinding[] bindings = new TypeBinding[providesTagsSize]; + int maxBindings = 0; + + // Scan all @provides tags + for (int i = 0; i < providesTagsSize; i++) { + TypeReference providesRef = (TypeReference)this.providesReferences[i]; + try { + providesRef.resolve(scope); + if (providesRef.resolvedType != null && providesRef.resolvedType.isValidBinding()) { + // Verify duplicated tags + boolean found = false; + for (int j = 0; j < maxBindings && !found; j++) { + if (bindings[j].equals(providesRef.resolvedType)) { + scope.problemReporter().javadocDuplicatedProvidesTag(providesRef.sourceStart, providesRef.sourceEnd); + found = true; + } + } + if (!found) { + bindings[maxBindings++] = providesRef.resolvedType; + } + } + } catch (Exception e) { + scope.problemReporter().javadocInvalidProvidesClass(providesRef.sourceStart, providesRef.sourceEnd); + } + } + + // Look for undocumented uses + if (reportMissing) { + for (int i = 0; i < providesSize; i++) { + ProvidesStatement provides = moduleDecl.services[i]; + boolean found = false; + for (int j = 0; j < maxBindings && !found; j++) { + TypeBinding binding = bindings[j]; + if (provides.serviceInterface.getTypeBinding(scope).equals(binding)) { + found = true; + } + } + if (!found) { + scope.problemReporter().javadocMissingProvidesTag(provides.serviceInterface, provides.sourceStart, provides.sourceEnd, moduleDecl.binding.modifiers); + } + } + } + } + } + + /* * Resolve @param tags for type parameters */ private void resolveTypeParameterTags(Scope scope, boolean reportMissing) { @@ -604,7 +753,7 @@ public class Javadoc extends ASTNode { // If no param tags then report a problem for each declaration type parameter if (parameters != null) { - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, avoid secondary errors when <= 1.4 + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, avoid secondary errors when <= 1.4 reportMissing = reportMissing && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; int typeParametersLength = parameters.length; if (paramTypeParamLength == 0) { @@ -830,7 +979,7 @@ public class Javadoc extends ASTNode { mainLoop: for (int i=0; i<length; i++) { char[][] compoundName = imports[i].compoundName; int compoundNameLength = compoundName.length; - if ((imports[i].onDemand && compoundNameLength == computedCompoundName.length-1) + if ((imports[i].onDemand && compoundNameLength == computedCompoundName.length-1) || (compoundNameLength == computedCompoundName.length)) { for (int j = compoundNameLength; --j >= 0;) { if (CharOperation.equals(imports[i].compoundName[j], computedCompoundName[j])) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocQualifiedTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocQualifiedTypeReference.java index 3ed38f0990..ef06cca0e4 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocQualifiedTypeReference.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocQualifiedTypeReference.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * 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 @@ -25,7 +25,7 @@ import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; -public class JavadocQualifiedTypeReference extends QualifiedTypeReference { +public class JavadocQualifiedTypeReference extends QualifiedTypeReference implements IJavadocTypeReference { public int tagSourceStart, tagSourceEnd; public PackageBinding packageBinding; @@ -72,7 +72,7 @@ public class JavadocQualifiedTypeReference extends QualifiedTypeReference { protected void reportDeprecatedType(TypeBinding type, Scope scope) { scope.problemReporter().javadocDeprecatedType(type, this, scope.getDeclarationModifiers()); } - + @Override protected void reportDeprecatedType(TypeBinding type, Scope scope, int index) { scope.problemReporter().javadocDeprecatedType(type, this, scope.getDeclarationModifiers(), index); @@ -107,4 +107,14 @@ public class JavadocQualifiedTypeReference extends QualifiedTypeReference { visitor.visit(this, scope); visitor.endVisit(this, scope); } + + @Override + public int getTagSourceStart() { + return this.tagSourceStart; + } + + @Override + public int getTagSourceEnd() { + return this.tagSourceEnd; + } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java index b64689b105..8a9ed4072d 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corporation and others. + * 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 @@ -29,7 +29,7 @@ import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; -public class JavadocSingleTypeReference extends SingleTypeReference { +public class JavadocSingleTypeReference extends SingleTypeReference implements IJavadocTypeReference { public int tagSourceStart, tagSourceEnd; public PackageBinding packageBinding; @@ -129,4 +129,14 @@ public class JavadocSingleTypeReference extends SingleTypeReference { visitor.visit(this, scope); visitor.endVisit(this, scope); } + + @Override + public int getTagSourceStart() { + return this.tagSourceStart; + } + + @Override + public int getTagSourceEnd() { + return this.tagSourceEnd; + } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java index 4f7a7b95b6..9b90a4f978 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java @@ -18,6 +18,7 @@ import java.util.List; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.InvalidInputException; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.util.Util; @@ -102,6 +103,14 @@ public abstract class AbstractCommentParser implements JavadocTagConstants { protected int astLengthPtr; protected int[] astLengthStack; + // Uses stack + protected int usesReferencesPtr = -1; + protected TypeReference[] usesReferencesStack; + + // Provides stack + protected int providesReferencesPtr = -1; + protected TypeReference[] providesReferencesStack; + protected AbstractCommentParser(Parser sourceParser) { this.sourceParser = sourceParser; @@ -456,6 +465,7 @@ public abstract class AbstractCommentParser implements JavadocTagConstants { } updateDocComment(); } catch (Exception ex) { + ex.printStackTrace(); validComment = false; } return validComment; @@ -501,7 +511,7 @@ public abstract class AbstractCommentParser implements JavadocTagConstants { return Util.getLineNumber(position, this.lineEnds, 0, this.lineEnds.length-1); } - private int getTokenEndPosition() { + protected int getTokenEndPosition() { if (this.scanner.getCurrentTokenEndPosition() > this.lineEnd) { return this.lineEnd; } else { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java index 8663caba08..0fe67daee5 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java @@ -17,7 +17,23 @@ import java.util.List; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.InvalidInputException; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.IJavadocTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Javadoc; +import org.eclipse.jdt.internal.compiler.ast.JavadocAllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.JavadocArgumentExpression; +import org.eclipse.jdt.internal.compiler.ast.JavadocArrayQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocArraySingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocFieldReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocImplicitTypeReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend; +import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.JavadocSingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.util.Util; @@ -27,6 +43,7 @@ import org.eclipse.jdt.internal.compiler.util.Util; public class JavadocParser extends AbstractCommentParser { private static final JavadocSingleNameReference[] NO_SINGLE_NAME_REFERENCE = new JavadocSingleNameReference[0]; private static final JavadocSingleTypeReference[] NO_SINGLE_TYPE_REFERENCE = new JavadocSingleTypeReference[0]; + private static final JavadocQualifiedTypeReference[] NO_QUALIFIED_TYPE_REFERENCE = new JavadocQualifiedTypeReference[0]; private static final TypeReference[] NO_TYPE_REFERENCE = new TypeReference[0]; private static final Expression[] NO_EXPRESSION = new Expression[0]; @@ -629,7 +646,9 @@ public class JavadocParser extends AbstractCommentParser { } } else if (length == TAG_PROVIDES_LENGTH && CharOperation.equals(TAG_PROVIDES, tagName, 0, length)) { this.tagValue = TAG_PROVIDES_VALUE; - this.tagWaitingForDescription = this.tagValue; + if (!this.inlineTagStarted) { + valid = parseProvidesReference(); + } } break; case 'r': @@ -677,7 +696,9 @@ public class JavadocParser extends AbstractCommentParser { case 'u': if (length == TAG_USES_LENGTH && CharOperation.equals(TAG_USES, tagName, 0, length)) { this.tagValue = TAG_USES_VALUE; - this.tagWaitingForDescription = this.tagValue; + if (!this.inlineTagStarted) { + valid = parseUsesReference(); + } } break; case 'v': @@ -951,6 +972,18 @@ public class JavadocParser extends AbstractCommentParser { System.arraycopy(this.invalidParamReferencesStack, 0, this.docComment.invalidParameters, 0, this.invalidParamReferencesPtr+1); } + this.docComment.usesReferences = this.usesReferencesPtr >= 0 ? new IJavadocTypeReference[this.usesReferencesPtr+1] : NO_QUALIFIED_TYPE_REFERENCE; + for (int i = 0; i <= this.usesReferencesPtr; ++i) { + TypeReference ref = this.usesReferencesStack[i]; + this.docComment.usesReferences[i] = (IJavadocTypeReference)ref; + } + + this.docComment.providesReferences = this.providesReferencesPtr >= 0 ? new IJavadocTypeReference[this.providesReferencesPtr+1] : NO_QUALIFIED_TYPE_REFERENCE; + for (int i = 0; i <= this.providesReferencesPtr; ++i) { + TypeReference ref = this.providesReferencesStack[i]; + this.docComment.providesReferences[i] = (IJavadocTypeReference)ref; + } + // If no nodes stored return if (this.astLengthPtr == -1) { return; @@ -1013,4 +1046,78 @@ public class JavadocParser extends AbstractCommentParser { System.arraycopy(this.docComment.paramTypeParameters, paramTypeParamPtr, this.docComment.paramTypeParameters = new JavadocSingleTypeReference[size - paramTypeParamPtr], 0, size - paramTypeParamPtr); } } + + /* + * Parse @uses tag declaration + */ + protected boolean parseUsesReference() { + int start = this.scanner.currentPosition; + try { + Object typeRef = parseQualifiedName(true); + if (this.abort) return false; // May be aborted by specialized parser + if (typeRef == null) { + if (this.reportProblems) + this.sourceParser.problemReporter().javadocMissingUsesClassName(this.tagSourceStart, this.tagSourceEnd, this.sourceParser.modifiers); + } else { + return pushUsesReference(typeRef); + } + } catch (InvalidInputException ex) { + if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidUsesClass(start, getTokenEndPosition()); + } + return false; + } + + protected boolean pushUsesReference(Object typeRef) { + // TODO Auto-generated method stub + if (this.usesReferencesPtr == -1l) { + this.usesReferencesStack = new TypeReference[10]; + } + int stackLength = this.usesReferencesStack.length; + if (++this.usesReferencesPtr >= stackLength) { + System.arraycopy( + this.usesReferencesStack, 0, + this.usesReferencesStack = new TypeReference[stackLength + AST_STACK_INCREMENT], 0, + stackLength); + } + this.usesReferencesStack[this.usesReferencesPtr] = (TypeReference)typeRef; + return true; + } + + /* + * Parse @uses tag declaration + */ + protected boolean parseProvidesReference() { + int start = this.scanner.currentPosition; + try { + Object typeRef = parseQualifiedName(true); + if (this.abort) return false; // May be aborted by specialized parser + if (typeRef == null) { + if (this.reportProblems) + this.sourceParser.problemReporter().javadocMissingProvidesClassName(this.tagSourceStart, this.tagSourceEnd, this.sourceParser.modifiers); + } else { + return pushProvidesReference(typeRef); + } + } catch (InvalidInputException ex) { + if (this.reportProblems) this.sourceParser.problemReporter().javadocInvalidProvidesClass(start, getTokenEndPosition()); + } + return false; + } + + protected boolean pushProvidesReference(Object typeRef) { + // TODO Auto-generated method stub + if (this.providesReferencesPtr == -1l) { + this.providesReferencesStack = new TypeReference[10]; + } + int stackLength = this.providesReferencesStack.length; + if (++this.providesReferencesPtr >= stackLength) { + System.arraycopy( + this.providesReferencesStack, 0, + this.providesReferencesStack = new TypeReference[stackLength + AST_STACK_INCREMENT], 0, + stackLength); + } + this.providesReferencesStack[this.providesReferencesPtr] = (TypeReference)typeRef; + return true; + + } + } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java index db0af41079..71f2eadf6a 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -5935,6 +5935,9 @@ protected void consumeModuleHeader() { } protected void consumeModuleDeclaration() { // ModuleDeclaration ::= ModuleHeader ModuleBody + this.compilationUnit.javadoc = this.javadoc; + this.javadoc = null; + int length = this.astLengthStack[this.astLengthPtr--]; int[] flag = new int[length + 1]; //plus one -- see <HERE> int size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index c227f5b093..d0bb1be1ef 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -546,6 +546,14 @@ public static int getIrritant(int problemID) { case IProblem.JavadocInvalidThrowsClassName: case IProblem.JavadocDuplicateThrowsClassName: case IProblem.JavadocMissingThrowsClassName: + case IProblem.JavadocDuplicateProvidesTag: + case IProblem.JavadocDuplicateUsesTag: + case IProblem.JavadocInvalidUsesClass: + case IProblem.JavadocInvalidUsesClassName: + case IProblem.JavadocInvalidProvidesClass: + case IProblem.JavadocInvalidProvidesClassName: + case IProblem.JavadocMissingProvidesClassName: + case IProblem.JavadocMissingUsesClassName: case IProblem.JavadocMissingSeeReference: case IProblem.JavadocInvalidValueReference: case IProblem.JavadocUndefinedField: @@ -589,8 +597,10 @@ public static int getIrritant(int problemID) { return CompilerOptions.InvalidJavadoc; case IProblem.JavadocMissingParamTag: + case IProblem.JavadocMissingProvidesTag: case IProblem.JavadocMissingReturnTag: case IProblem.JavadocMissingThrowsTag: + case IProblem.JavadocMissingUsesTag: return CompilerOptions.MissingJavadocTags; case IProblem.JavadocMissing: @@ -5219,6 +5229,9 @@ public void javadocDuplicatedParamTag(char[] token, int sourceStart, int sourceE sourceEnd); } } +public void javadocDuplicatedProvidesTag(int sourceStart, int sourceEnd){ + this.handle(IProblem.JavadocDuplicateProvidesTag, NoArgument, NoArgument, sourceStart, sourceEnd); +} public void javadocDuplicatedReturnTag(int sourceStart, int sourceEnd){ this.handle(IProblem.JavadocDuplicateReturnTag, NoArgument, NoArgument, sourceStart, sourceEnd); } @@ -5245,6 +5258,11 @@ public void javadocDuplicatedThrowsClassName(TypeReference typeReference, int mo typeReference.sourceEnd); } } +public void javadocDuplicatedUsesTag( + + int sourceStart, int sourceEnd){ + this.handle(IProblem.JavadocDuplicateUsesTag, NoArgument, NoArgument, sourceStart, sourceEnd); +} public void javadocEmptyReturnTag(int sourceStart, int sourceEnd, int modifiers) { int severity = computeSeverity(IProblem.JavadocEmptyReturnTag); if (severity == ProblemSeverities.Ignore) return; @@ -5692,6 +5710,24 @@ public void javadocInvalidParamTagName(int sourceStart, int sourceEnd) { public void javadocInvalidParamTypeParameter(int sourceStart, int sourceEnd) { this.handle(IProblem.JavadocInvalidParamTagTypeParameter, NoArgument, NoArgument, sourceStart, sourceEnd); } +public void javadocInvalidProvidesClass(int sourceStart, int sourceEnd) { + this.handle(IProblem.JavadocInvalidProvidesClass, NoArgument, NoArgument, sourceStart, sourceEnd); +} + +public void javadocInvalidProvidesClassName(TypeReference typeReference, int modifiers) { + int severity = computeSeverity(IProblem.JavadocInvalidProvidesClassName); + if (severity == ProblemSeverities.Ignore) return; + if (javadocVisibility(this.options.reportInvalidJavadocTagsVisibility, modifiers)) { + String[] arguments = new String[] {String.valueOf(typeReference.resolvedType.sourceName())}; + this.handle( + IProblem.JavadocInvalidProvidesClassName, + arguments, + arguments, + severity, + typeReference.sourceStart, + typeReference.sourceEnd); + } +} public void javadocInvalidReference(int sourceStart, int sourceEnd) { this.handle(IProblem.JavadocInvalidSeeReference, NoArgument, NoArgument, sourceStart, sourceEnd); } @@ -5770,6 +5806,24 @@ public void javadocInvalidType(ASTNode location, TypeBinding type, int modifiers location.sourceEnd); } } +public void javadocInvalidUsesClass(int sourceStart, int sourceEnd) { + this.handle(IProblem.JavadocInvalidUsesClass, NoArgument, NoArgument, sourceStart, sourceEnd); +} + +public void javadocInvalidUsesClassName(TypeReference typeReference, int modifiers) { + int severity = computeSeverity(IProblem.JavadocInvalidUsesClassName); + if (severity == ProblemSeverities.Ignore) return; + if (javadocVisibility(this.options.reportInvalidJavadocTagsVisibility, modifiers)) { + String[] arguments = new String[] {String.valueOf(typeReference.resolvedType.sourceName())}; + this.handle( + IProblem.JavadocInvalidUsesClassName, + arguments, + arguments, + severity, + typeReference.sourceStart, + typeReference.sourceEnd); + } +} public void javadocInvalidValueReference(int sourceStart, int sourceEnd, int modifiers) { if (javadocVisibility(this.options.reportInvalidJavadocTagsVisibility, modifiers)) this.handle(IProblem.JavadocInvalidValueReference, NoArgument, NoArgument, sourceStart, sourceEnd); @@ -5800,6 +5854,20 @@ public void javadocMissing(int sourceStart, int sourceEnd, int severity, int mod } } } +public void javadocModuleMissing(int sourceStart, int sourceEnd, int severity){ + if (severity == ProblemSeverities.Ignore) return; + boolean report = this.options.getSeverity(CompilerOptions.MissingJavadocComments) != ProblemSeverities.Ignore; + if (report) { + String[] arguments = new String[] { "module" }; //$NON-NLS-1$ + this.handle( + IProblem.JavadocMissing, + arguments, + arguments, + severity, + sourceStart, + sourceEnd); + } +} public void javadocMissingHashCharacter(int sourceStart, int sourceEnd, String ref){ int severity = computeSeverity(IProblem.JavadocMissingHashCharacter); if (severity == ProblemSeverities.Ignore) return; @@ -5837,6 +5905,26 @@ public void javadocMissingParamTag(char[] name, int sourceStart, int sourceEnd, sourceEnd); } } +public void javadocMissingProvidesClassName(int sourceStart, int sourceEnd, int modifiers){ + if (javadocVisibility(this.options.reportInvalidJavadocTagsVisibility, modifiers)) { + this.handle(IProblem.JavadocMissingProvidesClassName, NoArgument, NoArgument, sourceStart, sourceEnd); + } +} +public void javadocMissingProvidesTag(TypeReference typeRef, int sourceStart, int sourceEnd, int modifiers){ + int severity = computeSeverity(IProblem.JavadocMissingProvidesTag); + if (severity == ProblemSeverities.Ignore) return; + boolean report = this.options.getSeverity(CompilerOptions.MissingJavadocTags) != ProblemSeverities.Ignore; + if (report) { + String[] arguments = new String[] { String.valueOf(typeRef.resolvedType.sourceName()) }; + this.handle( + IProblem.JavadocMissingProvidesTag, + arguments, + arguments, + severity, + sourceStart, + sourceEnd); + } +} public void javadocMissingReference(int sourceStart, int sourceEnd, int modifiers){ if (javadocVisibility(this.options.reportInvalidJavadocTagsVisibility, modifiers)) this.handle(IProblem.JavadocMissingSeeReference, NoArgument, NoArgument, sourceStart, sourceEnd); @@ -5887,6 +5975,27 @@ public void javadocMissingThrowsTag(TypeReference typeRef, int modifiers){ typeRef.sourceEnd); } } +public void javadocMissingUsesClassName(int sourceStart, int sourceEnd, int modifiers){ + if (javadocVisibility(this.options.reportInvalidJavadocTagsVisibility, modifiers)) { + this.handle(IProblem.JavadocMissingUsesClassName, NoArgument, NoArgument, sourceStart, sourceEnd); + } +} + +public void javadocMissingUsesTag(TypeReference typeRef, int sourceStart, int sourceEnd, int modifiers){ + int severity = computeSeverity(IProblem.JavadocMissingUsesTag); + if (severity == ProblemSeverities.Ignore) return; + boolean report = this.options.getSeverity(CompilerOptions.MissingJavadocTags) != ProblemSeverities.Ignore; + if (report) { + String[] arguments = new String[] { String.valueOf(typeRef.resolvedType.sourceName()) }; + this.handle( + IProblem.JavadocMissingUsesTag, + arguments, + arguments, + severity, + sourceStart, + sourceEnd); + } +} public void javadocUndeclaredParamTagName(char[] token, int sourceStart, int sourceEnd, int modifiers) { int severity = computeSeverity(IProblem.JavadocInvalidParamName); if (severity == ProblemSeverities.Ignore) return; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties index c859ddd4db..8c4171797b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1013,6 +1013,18 @@ 1717 = yield may be a restricted identifier in future and may be disallowed as a type name 1718 = yield is a restricted identifier and cannot be used as type name +# Additional doc +1800 = Missing uses tag +1801 = Duplicate uses tag +1802 = Missing uses class name +1803 = Invalid uses class name +1804 = Invalid uses class +1805 = Missing provides tag +1806 = Duplicate provides tag +1807 = Missing provides class name +1808 = Invalid provides class name +1809 = Invalid provides class + ### ELABORATIONS ## Access restrictions 78592 = The type ''{1}'' is not API (restriction on classpath entry ''{0}'') diff --git a/org.eclipse.jdt.core/jdtCompilerAdapter.jar b/org.eclipse.jdt.core/jdtCompilerAdapter.jar Binary files differindex f5a110881e..dd21253fb9 100644 --- a/org.eclipse.jdt.core/jdtCompilerAdapter.jar +++ b/org.eclipse.jdt.core/jdtCompilerAdapter.jar |