-
Notifications
You must be signed in to change notification settings - Fork 135
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
[improvement] Enforce guava and objects calls with no parameters and constant strings use logsafe #517
Conversation
To address #507 |
@@ -62,8 +75,6 @@ public final void validGuava() throws Exception { | |||
"class Test {", | |||
" private static final String compileTimeConstant = \"constant\";", | |||
" void f(boolean bArg, int iArg, Object oArg) {", | |||
" Preconditions.checkArgument(bArg);", | |||
" Preconditions.checkArgument(bArg, \"message\");", |
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 don't think this should be modified, it is valid for the existing test, just not the new check
|
||
@AutoService(BugChecker.class) | ||
@BugPattern( | ||
name = "GuavaPreconditionsEnforceLogSafeUse", |
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.
We're really checking one thing: functionality that can be replaced with safe-logging utilities without losing any information/utility. I'd prefer to have a single check for that.
Based on the existing PreferSafeLoggableExceptions
check we should call it PreferSafeLoggingPreconditions
.
@BugPattern( | ||
name = "GuavaPreconditionsEnforceLogSafeUse", | ||
category = BugPattern.Category.ONE_OFF, | ||
severity = BugPattern.SeverityLevel.ERROR, |
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.
Based on PreferSafeLoggableExceptions
we should use SeverityLevel.SUGGESTION
by default, in certain large internal codebases we will override it to ERROR. Longer term we should set the new check and PreferSafeLoggableExceptions to error, but I'd like to decouple that from this change.
|
||
if (args.size() == 2) { | ||
ExpressionTree messageArg = args.get(1); | ||
if (!compileTimeConstExpressionMatcher.matches(messageArg, state)) { |
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 needs to check the type of args[1], otherwise we'll get false positives on Objects.requireNonNull(obj, () -> calculateExpensiveMessage())
using the supplier overload.
category = BugPattern.Category.ONE_OFF, | ||
severity = BugPattern.SeverityLevel.ERROR, | ||
summary = "Objects.requireNonNull(Object) and Objects.requireNonNull(Object, String) methods with a " | ||
+ "constant message and no parameters should use logsafe Preconditions.checkNotNull(Object, String)") |
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.
might as well give the FQN: com.palantir.logsafe.Preconditions#checkNotNull(Object, String)
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.
Also I'd recommend giving a little hint as to why error-prone is recommending the change, e.g. the PreferSafeLoggableExceptions rule says "Throw SafeLoggable exceptions to ensure the exception message will not be redacted"
Matchers.anyOf( | ||
MethodMatchers.staticMethod() | ||
.onClass("com.google.common.base.Preconditions") | ||
.withNameMatching(Pattern.compile("checkArgument|checkState|checkNotNull"))); |
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.
Probably a separate PR, but would be good to check Guava’s Verify.verify
& Verify.verifyNotNull
as well as commons-lang Validate.*
?
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.
That would be difficult -- guava Preconditions throws standard IllegalState/IllegalArg/NullPointer exceptions, but these methods throw library-specific exception types, so a migration could break existing catches.
I think we should suggest against using them in favor of safe-logging preconditions, but I'd prefer to do so in a different check.
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.
👍 Makes sense for Guava's Verify
and I expect much lower usage of that.
The commons-lang Validate
is much more heavily used in some places, and those should be straightforward replacements by equivalent Safe
preconditions
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 had though lang3 validate used custom exception types, thanks for the correction! In that case it makes sense to add common methods to this check. Let's add it in a separate PR.
f484ec9
to
36f119a
Compare
.replace(tree, String.format( | ||
"com.palantir.logsafe.Preconditions.checkNotNull(%s)", tree.getArguments())) | ||
.build()) | ||
.build(); |
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.
Can we combine these? The message is different, but doesn't need to be because we output a suggested change which illustrates the current and proposed states. We can add a function which maps from a given method name to a safe-logging Preconditions method name instead.
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.
👍
|
||
if (args.size() == 2) { | ||
ExpressionTree messageArg = args.get(1); | ||
if (!messageArg.getKind().equals(Tree.Kind.STRING_LITERAL) |
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.
Do we actually want to test that this is a string literal? Will that catch references to compile-time constant methods?
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.
Good catch! Yeah, that won't work with compile-time constants. Will fix.
if (args.size() == 2) { | ||
ExpressionTree messageArg = args.get(1); | ||
if (!messageArg.getKind().equals(Tree.Kind.STRING_LITERAL) | ||
|| !compileTimeConstExpressionMatcher.matches(messageArg, state)) { |
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 think we should check the method matchers prior to this, I imagine the compile time constant matcher is more expensive than checking method names, and this will fire for all 2-arg methods with a string as the second argument.
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 check should be added to the list of exclusions for gradle plugins, since those don't use safe-logging |
…s and constant strings
36f119a
to
8a31435
Compare
README.md
Outdated
- `PreferSafeLoggingPreconditions`: Users should use the safe-logging versions of Precondition checks for standardization when there is equivalent functionality | ||
```diff | ||
-com.google.common.base.Preconditions.checkNotNull(variable, "message"); | ||
-com.palantir.logsafe.Preconditions.checkNotNull(variable, "message"); // equivalent functionality is available in the safe-logging variant |
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.
Should be a "+"
Please don't force-push, that makes it hard to review diffs! |
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.
Looks solid to me :) I'm looking forward to actually exposing those suggested fixes!
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.
lgtm, thanks @dsd987!
Before this PR
There is no standardization in the use of basic calls to verify conditions like guava's Preconditions.check* or Objects.requireNonNull.
After this PR
Simple use cases of guava Preconditions.check* and Objects.requireNonNull are now required to be changed to use logsafe variants. This will help to standardize calls where it is clear there can be a direct one-for-one replacement made with logsafe calls.