diff --git a/check_api/src/main/java/com/google/errorprone/ErrorProneAnalyzer.java b/check_api/src/main/java/com/google/errorprone/ErrorProneAnalyzer.java index 8835cc0f869..c62c8032cc9 100644 --- a/check_api/src/main/java/com/google/errorprone/ErrorProneAnalyzer.java +++ b/check_api/src/main/java/com/google/errorprone/ErrorProneAnalyzer.java @@ -24,6 +24,7 @@ import com.google.common.base.Throwables; import com.google.errorprone.scanner.ErrorProneScannerTransformer; import com.google.errorprone.scanner.ScannerSupplier; +import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.Tree; import com.sun.source.util.TaskEvent; @@ -38,8 +39,10 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.PropagatedException; +import java.net.URI; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; /** A {@link TaskListener} that runs Error Prone over attributed compilation units. */ @Trusted @@ -134,15 +137,17 @@ public void finished(TaskEvent taskEvent) { DescriptionListener descriptionListener = descriptionListenerFactory.getDescriptionListener(log, compilation); try { - if (path.getLeaf().getKind() == Tree.Kind.COMPILATION_UNIT) { - // We only get TaskEvents for compilation units if they contain no package declarations - // (e.g. package-info.java files). In this case it's safe to analyze the - // CompilationUnitTree immediately. - transformer.get().apply(path, subContext, descriptionListener); - } else if (finishedCompilation(path.getCompilationUnit())) { - // Otherwise this TaskEvent is for a ClassTree, and we can scan the whole - // CompilationUnitTree once we've seen all the enclosed classes. - transformer.get().apply(new TreePath(compilation), subContext, descriptionListener); + if (!isExcludedPath(compilation.getSourceFile().toUri())) { + if (path.getLeaf().getKind() == Tree.Kind.COMPILATION_UNIT) { + // We only get TaskEvents for compilation units if they contain no package declarations + // (e.g. package-info.java files). In this case it's safe to analyze the + // CompilationUnitTree immediately. + transformer.get().apply(path, subContext, descriptionListener); + } else if (finishedCompilation(path.getCompilationUnit())) { + // Otherwise this TaskEvent is for a ClassTree, and we can scan the whole + // CompilationUnitTree once we've seen all the enclosed classes. + transformer.get().apply(new TreePath(compilation), subContext, descriptionListener); + } } } catch (ErrorProneError e) { e.logFatalError(log); @@ -160,7 +165,17 @@ public void finished(TaskEvent taskEvent) { } } - /** Returns true if all declarations inside the given compilation unit have been visited. */ + /** + * Returns true if all declarations inside the given compilation unit have been visited. + */ + private boolean isExcludedPath(URI uri) { + Pattern excludedPattern = errorProneOptions.getExcludedPattern(); + return (excludedPattern != null) && excludedPattern.matcher(ASTHelpers.getFileNameFromUri(uri)).matches(); + } + + /** + * Returns true if all declarations inside the given compilation unit have been visited. + */ private boolean finishedCompilation(CompilationUnitTree tree) { OUTER: for (Tree decl : tree.getTypeDecls()) { diff --git a/check_api/src/main/java/com/google/errorprone/ErrorProneOptions.java b/check_api/src/main/java/com/google/errorprone/ErrorProneOptions.java index b21111025f3..8a8baa44f1a 100644 --- a/check_api/src/main/java/com/google/errorprone/ErrorProneOptions.java +++ b/check_api/src/main/java/com/google/errorprone/ErrorProneOptions.java @@ -17,6 +17,7 @@ package com.google.errorprone; import com.google.auto.value.AutoValue; +import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; @@ -33,8 +34,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; /** * Processes command-line options specific to error-prone. @@ -56,6 +59,7 @@ public class ErrorProneOptions { private static final String DISABLE_WARNINGS_IN_GENERATED_CODE_FLAG = "-XepDisableWarningsInGeneratedCode"; private static final String COMPILING_TEST_ONLY_CODE = "-XepCompilingTestOnlyCode"; + private static final String EXCLUDED_PATHS_PREFIX = "-XepExcludedPaths:"; /** see {@link javax.tools.OptionChecker#isSupportedOption(String)} */ public static int isSupportedOption(String option) { @@ -64,6 +68,7 @@ public static int isSupportedOption(String option) { || option.startsWith(ErrorProneFlags.PREFIX) || option.startsWith(PATCH_OUTPUT_LOCATION) || option.startsWith(PATCH_CHECKS_PREFIX) + || option.startsWith(EXCLUDED_PATHS_PREFIX) || option.equals(IGNORE_UNKNOWN_CHECKS_FLAG) || option.equals(DISABLE_WARNINGS_IN_GENERATED_CODE_FLAG) || option.equals(ERRORS_AS_WARNINGS_FLAG) @@ -156,6 +161,7 @@ final PatchingOptions build() { private final boolean isTestOnlyTarget; private final ErrorProneFlags flags; private final PatchingOptions patchingOptions; + private final Pattern excludedPattern; private ErrorProneOptions( ImmutableMap severityMap, @@ -167,7 +173,8 @@ private ErrorProneOptions( boolean disableAllChecks, boolean isTestOnlyTarget, ErrorProneFlags flags, - PatchingOptions patchingOptions) { + PatchingOptions patchingOptions, + Pattern excludedPattern) { this.severityMap = severityMap; this.remainingArgs = remainingArgs; this.ignoreUnknownChecks = ignoreUnknownChecks; @@ -178,6 +185,7 @@ private ErrorProneOptions( this.isTestOnlyTarget = isTestOnlyTarget; this.flags = flags; this.patchingOptions = patchingOptions; + this.excludedPattern = excludedPattern; } public String[] getRemainingArgs() { @@ -212,6 +220,10 @@ public PatchingOptions patchingOptions() { return patchingOptions; } + public Pattern getExcludedPattern() { + return excludedPattern; + } + private static class Builder { private boolean ignoreUnknownChecks = false; private boolean disableWarningsInGeneratedCode = false; @@ -222,6 +234,7 @@ private static class Builder { private Map severityMap = new HashMap<>(); private final ErrorProneFlags.Builder flagsBuilder = ErrorProneFlags.builder(); private final PatchingOptions.Builder patchingOptionsBuilder = PatchingOptions.builder(); + private Pattern excludedPattern; private void parseSeverity(String arg) { // Strip prefix @@ -301,7 +314,12 @@ public ErrorProneOptions build(ImmutableList remainingArgs) { disableAllChecks, isTestOnlyTarget, flagsBuilder.build(), - patchingOptionsBuilder.build()); + patchingOptionsBuilder.build(), + excludedPattern); + } + + public void setExcludedPattern(Pattern excludedPattern) { + this.excludedPattern = excludedPattern; } } @@ -391,6 +409,9 @@ public static ErrorProneOptions processArgs(Iterable args) { String remaining = arg.substring(PATCH_IMPORT_ORDER_PREFIX.length()); ImportOrganizer importOrganizer = ImportOrderParser.getImportOrganizer(remaining); builder.patchingOptionsBuilder().importOrganizer(importOrganizer); + } else if (arg.startsWith(EXCLUDED_PATHS_PREFIX)) { + String pathRegex = arg.substring(EXCLUDED_PATHS_PREFIX.length()); + builder.setExcludedPattern(Pattern.compile(pathRegex)); } else { remainingArgs.add(arg); } diff --git a/check_api/src/test/java/com/google/errorprone/ErrorProneOptionsTest.java b/check_api/src/test/java/com/google/errorprone/ErrorProneOptionsTest.java index ef3630716f2..5eed074273f 100644 --- a/check_api/src/test/java/com/google/errorprone/ErrorProneOptionsTest.java +++ b/check_api/src/test/java/com/google/errorprone/ErrorProneOptionsTest.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -163,6 +164,22 @@ public void recognizesCompilingTestOnlyCode() { assertThat(options.isTestOnlyTarget()).isTrue(); } + @Test + public void recognizesExcludedPaths() { + ErrorProneOptions options = + ErrorProneOptions.processArgs( + new String[] {"-XepExcludedPaths:(.*/)?(build/generated|other_output)/.*\\.java"}); + Pattern excludedPattern = options.getExcludedPattern(); + assertThat(excludedPattern).isNotNull(); + assertThat(excludedPattern.matcher("fizz/build/generated/Gen.java").matches()).isTrue(); + assertThat(excludedPattern.matcher("fizz/bazz/generated/Gen.java").matches()).isFalse(); + assertThat(excludedPattern.matcher("fizz/abuild/generated/Gen.java").matches()).isFalse(); + assertThat(excludedPattern.matcher("other_output/Gen.java").matches()).isTrue(); + assertThat(excludedPattern.matcher("foo/other_output/subdir/Gen.java").matches()).isTrue(); + assertThat(excludedPattern.matcher("foo/other_output/subdir/Gen.cpp").matches()).isFalse(); + } + + @Test public void recognizesPatch() { ErrorProneOptions options = diff --git a/core/src/test/java/com/google/errorprone/ErrorProneJavaCompilerTest.java b/core/src/test/java/com/google/errorprone/ErrorProneJavaCompilerTest.java index 80466b3dcf0..921b5846b63 100644 --- a/core/src/test/java/com/google/errorprone/ErrorProneJavaCompilerTest.java +++ b/core/src/test/java/com/google/errorprone/ErrorProneJavaCompilerTest.java @@ -398,6 +398,32 @@ public void testFixGeneratedConstructor() throws Exception { .contains("AssertionError: Cannot edit synthetic AST nodes"); } + @Test + public void testWithExcludedPaths() throws Exception { + CompilationResult result = + doCompile( + Arrays.asList("bugpatterns/testdata/SelfAssignmentPositiveCases1.java"), + Collections.emptyList(), + Collections.>emptyList()); + assertThat(result.succeeded).isFalse(); + + result = + doCompile( + Arrays.asList("bugpatterns/testdata/SelfAssignmentPositiveCases1.java"), + Arrays.asList("-XepExcludedPaths:.*/bugpatterns/.*"), + Collections.>emptyList()); + assertThat(result.succeeded).isTrue(); + + // ensure regexp must match the full path + result = + doCompile( + Arrays.asList("bugpatterns/testdata/SelfAssignmentPositiveCases1.java"), + Arrays.asList("-XepExcludedPaths:bugpatterns"), + Collections.>emptyList()); + assertThat(result.succeeded).isFalse(); + } + + private static class CompilationResult { public final boolean succeeded; public final DiagnosticTestHelper diagnosticHelper;