-
Notifications
You must be signed in to change notification settings - Fork 746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Propagate check flags in patch mode #4699
base: master
Are you sure you want to change the base?
Propagate check flags in patch mode #4699
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this issue while working on PicnicSupermarket/error-prone-support#1426, where I noticed that a custom flag -XepOpt:Slf4jLogDeclaration:CanonicalStaticLoggerName=LOGGER
was respected during regular compilation, but not when in patch mode.
ErrorPronePlugins.loadPlugins(scannerSupplier, context); | ||
ErrorPronePlugins.loadPlugins(scannerSupplier, context) | ||
.applyOverrides(epOptions); | ||
ImmutableSet<String> namedCheckers = | ||
epOptions.patchingOptions().namedCheckers(); | ||
if (!namedCheckers.isEmpty()) { | ||
toUse = toUse.filter(bci -> namedCheckers.contains(bci.canonicalName())); | ||
} else { | ||
toUse = toUse.applyOverrides(epOptions); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides that all existing tests still pass: this change seems reasonable, at least on a conceptual level. I didn't thoroughly assess whether the non-flag changes applied by applyOverrides
are no-ops, though. Could have a closer look at that later.
assertThat(Files.readString(location)) | ||
.isEqualTo( | ||
""" | ||
class StringConstantWrapper { | ||
String s = "new-value"; | ||
} | ||
"""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without the fix above, this test fails with:
[ERROR] Failures:
[ERROR] ErrorProneJavaCompilerTest.patchWithBugPatternCustomization:465 value of:
readString(...)
expected:
class StringConstantWrapper {
String s = "new-value";
}
but was:
class StringConstantWrapper {
String s = "flag-not-set";
}
An issue that's likely fixed by this change was just reported against NullAway: uber/NullAway#1080. CC @cushon; might be worthy of a new release. |
Effectively this change reverts #4028, though the unit tests added there still pass. So I suppose that "something else" changed that made things compatible 👀 |
Would it be better for Error Prone to detect the case where a disabled check is passed to |
A better error message may indeed be acceptable in some cases, though both the reporter of #3908 and the developer of #4028 (back then a working student at Picnic) aim to see patching of disabled checks skipped for automation purposes. So hopefully we can find a way to have our cake and eat it too. (Managing expectations: I'm currently moving apartments, so won't have time to take a closer look in the coming days. Though it's a bit surprising that it took nearly nine months for this issue to be surfaced, one could argue that not fixing this issue is worse than re-introducing the other bug (as this impacts at least one use case for "normal" users: NullAway), and that thus a release with a revert of #4028 (possibly including a revert of its tests, that apparently no longer guard against regression) is the right thing to do for now.) |
c54e413
to
574cea1
Compare
574cea1
to
205c5a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rebased the PR and added a second commit. I think this PR is now ready for review. @cushon if deemed acceptable, it'd be nice to have a new release with this change. 🙏
Happy holidays!
@Test | ||
public void dontRunPatchForDisabledChecks() { | ||
compilationHelper | ||
.addSourceLines( | ||
"Test.java", | ||
""" | ||
import com.google.errorprone.scanner.ScannerTest.Foo; | ||
|
||
class Test { | ||
Foo foo; | ||
} | ||
""") | ||
.setArgs("-XepPatchLocation:IN_PLACE", "-XepPatchChecks:", "-Xep:ShouldNotUseFoo:OFF") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void dontRunPatchUnlessRequested() { | ||
compilationHelper | ||
.addSourceLines( | ||
"Test.java", | ||
""" | ||
import com.google.errorprone.scanner.ScannerTest.Foo; | ||
|
||
class Test { | ||
Foo foo; | ||
} | ||
""") | ||
.setArgs( | ||
"-XepPatchLocation:IN_PLACE", | ||
"-Xep:ShouldNotUseFoo:WARN", | ||
"-XepPatchChecks:DeadException") | ||
.doTest(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Test | ||
public void patchSingleWithCheckDisabled() throws IOException { | ||
JavaFileObject fileObject = | ||
createOnDiskFileObject( | ||
"StringConstantWrapper.java", | ||
""" | ||
class StringConstantWrapper { | ||
String s = "old-value"; | ||
} | ||
"""); | ||
|
||
CompilationResult result = | ||
doCompile( | ||
Collections.singleton(fileObject), | ||
Arrays.asList( | ||
"-XepPatchChecks:AssignmentUpdater", | ||
"-XepPatchLocation:IN_PLACE", | ||
"-Xep:AssignmentUpdater:OFF"), | ||
Collections.singletonList(AssignmentUpdater.class)); | ||
assertThat(result.succeeded).isTrue(); | ||
assertThat(Files.readString(Path.of(fileObject.toUri()))) | ||
.isEqualTo( | ||
""" | ||
class StringConstantWrapper { | ||
String s = "old-value"; | ||
} | ||
"""); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new test covers against regression of #3908: without the latest commit, it fails.
❗ That said, my changes impact semantics ❗. Before the changes in this PR, Error Prone would ignore the -Xep:AssignmentUpdater:OFF
flag and apply any suggestions by AssignmentUpdater
. After my changes, AssignmentUpdater
is ignored altogether.
I can see that both the old and new behavior are desirable in different contexts. However, any user that desires the previous behavior during patching can append -XepPatchChecks:AssignmentUpdater -Xep:AssignmentUpdater:ERROR
instead of just -XepPatchChecks:AssignmentUpdater
(because "last flag wins"). So the new semantics make it easier to write a script/integration that allows one to patch for some check C
across a large set of projects, conditionally ignoring any projects that explicitly disable C
.
@Test | ||
public void patchSingleWithBugPatternCustomization() throws IOException { | ||
JavaFileObject fileObject = | ||
createOnDiskFileObject( | ||
"StringConstantWrapper.java", | ||
""" | ||
class StringConstantWrapper { | ||
String s = "old-value"; | ||
} | ||
"""); | ||
|
||
CompilationResult result = | ||
doCompile( | ||
Collections.singleton(fileObject), | ||
Arrays.asList( | ||
"-XepPatchChecks:AssignmentUpdater", | ||
"-XepPatchLocation:IN_PLACE", | ||
"-XepOpt:AssignmentUpdater:NewValue=new-value"), | ||
Collections.singletonList(AssignmentUpdater.class)); | ||
assertThat(result.succeeded).isTrue(); | ||
assertThat(Files.readString(Path.of(fileObject.toUri()))) | ||
.isEqualTo( | ||
""" | ||
class StringConstantWrapper { | ||
String s = "new-value"; | ||
} | ||
"""); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new test covers the issue for which I opened the PR (and described by uber/NullAway#1080).
@Test | ||
public void patchAll() throws IOException { | ||
JavaFileObject fileObject = | ||
createOnDiskFileObject( | ||
"StringConstantWrapper.java", | ||
""" | ||
class StringConstantWrapper { | ||
String s = "old-value"; | ||
} | ||
"""); | ||
|
||
CompilationResult result = | ||
doCompile( | ||
Collections.singleton(fileObject), | ||
Arrays.asList("-XepPatchChecks:", "-XepPatchLocation:IN_PLACE"), | ||
Collections.singletonList(AssignmentUpdater.class)); | ||
assertThat(result.succeeded).isTrue(); | ||
assertThat(Files.readString(Path.of(fileObject.toUri()))) | ||
.isEqualTo( | ||
""" | ||
class StringConstantWrapper { | ||
String s = "flag-not-set"; | ||
} | ||
"""); | ||
} | ||
|
||
@Test | ||
public void patchAllWithCheckDisabled() throws IOException { | ||
JavaFileObject fileObject = | ||
createOnDiskFileObject( | ||
"StringConstantWrapper.java", | ||
""" | ||
class StringConstantWrapper { | ||
String s = "old-value"; | ||
} | ||
"""); | ||
|
||
CompilationResult result = | ||
doCompile( | ||
Collections.singleton(fileObject), | ||
Arrays.asList( | ||
"-XepPatchChecks:", "-XepPatchLocation:IN_PLACE", "-Xep:AssignmentUpdater:OFF"), | ||
Collections.singletonList(AssignmentUpdater.class)); | ||
assertThat(result.succeeded).isTrue(); | ||
assertThat(Files.readString(Path.of(fileObject.toUri()))) | ||
.isEqualTo( | ||
""" | ||
class StringConstantWrapper { | ||
String s = "old-value"; | ||
} | ||
"""); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two tests describe the "common case": they pass both before and after the fix.
.or( | ||
Suppliers.memoize( | ||
() -> { | ||
ScannerSupplier toUse = | ||
ErrorPronePlugins.loadPlugins(scannerSupplier, context); | ||
ImmutableSet<String> namedCheckers = | ||
epOptions.patchingOptions().namedCheckers(); | ||
if (!namedCheckers.isEmpty()) { | ||
toUse = toUse.filter(bci -> namedCheckers.contains(bci.canonicalName())); | ||
} else { | ||
toUse = toUse.applyOverrides(epOptions); | ||
} | ||
ScannerSupplier toUse = | ||
ErrorPronePlugins.loadPlugins(scannerSupplier, context) | ||
.applyOverrides(epOptions) | ||
.filter( | ||
bci -> { | ||
String name = bci.canonicalName(); | ||
return epOptions.getSeverityMap().get(name) != Severity.OFF | ||
&& (namedCheckers.isEmpty() | ||
|| namedCheckers.contains(name)); | ||
}); | ||
return ErrorProneScannerTransformer.create(toUse.get()); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So patching now ignores checks:
- Whose severity is
OFF
, or - That are not explicitly enabled for patching, while other checks are.
No description provided.