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..eddbf733802 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; @@ -40,6 +41,8 @@ import com.sun.tools.javac.util.PropagatedException; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; +import javax.tools.JavaFileObject; /** A {@link TaskListener} that runs Error Prone over attributed compilation units. */ @Trusted @@ -134,6 +137,9 @@ public void finished(TaskEvent taskEvent) { DescriptionListener descriptionListener = descriptionListenerFactory.getDescriptionListener(log, compilation); try { + if (shouldExcludeSourceFile(compilation.getSourceFile())) { + return; + } 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 @@ -160,6 +166,13 @@ public void finished(TaskEvent taskEvent) { } } + /** Returns true if the given source file should be excluded from analysis. */ + private boolean shouldExcludeSourceFile(JavaFileObject sourceFile) { + Pattern excludedPattern = errorProneOptions.getExcludedPattern(); + return excludedPattern != null + && excludedPattern.matcher(ASTHelpers.getFileNameFromUri(sourceFile.toUri())).matches(); + } + /** Returns true if all declarations inside the given compilation unit have been visited. */ private boolean finishedCompilation(CompilationUnitTree tree) { OUTER: 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..c404147bda9 100644 --- a/check_api/src/main/java/com/google/errorprone/ErrorProneOptions.java +++ b/check_api/src/main/java/com/google/errorprone/ErrorProneOptions.java @@ -35,6 +35,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; /** * Processes command-line options specific to error-prone. @@ -56,6 +57,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 +66,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 +159,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 +171,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 +183,7 @@ private ErrorProneOptions( this.isTestOnlyTarget = isTestOnlyTarget; this.flags = flags; this.patchingOptions = patchingOptions; + this.excludedPattern = excludedPattern; } public String[] getRemainingArgs() { @@ -212,6 +218,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 +232,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 +312,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 +407,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..e9c2888efbb 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,21 @@ 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..5f22d62ec81 100644 --- a/core/src/test/java/com/google/errorprone/ErrorProneJavaCompilerTest.java +++ b/core/src/test/java/com/google/errorprone/ErrorProneJavaCompilerTest.java @@ -398,6 +398,31 @@ 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;