Skip to content
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: suggested fixes for IllegalSafeLoggingArgument check #2133

Merged
merged 3 commits into from
Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.method.MethodMatchers;
Expand All @@ -38,8 +40,6 @@
* Ensures that safe-logging annotated elements are handled correctly by annotated method parameters.
* Potential future work:
* <ul>
* <li>Suggest replacing {@code UnsafeArg.of(name, verifiableSafeValue)} with
* {@code SafeArg.of(name, verifiableSafeValue)}</li>
* <li>We could check return statements in methods annotated for
* safety to require consistency</li>
* <li>Enforce propagation of safety annotations from fields and types to types which encapsulate them.</li>
Expand All @@ -58,6 +58,11 @@ public final class IllegalSafeLoggingArgument extends BugChecker implements BugC
private static final String UNSAFE = "com.palantir.logsafe.Unsafe";
private static final String DO_NOT_LOG = "com.palantir.logsafe.DoNotLog";

private static final String UNSAFE_ARG = "com.palantir.logsafe.UnsafeArg";
private static final Matcher<ExpressionTree> SAFE_ARG_OF_METHOD_MATCHER = MethodMatchers.staticMethod()
.onClass("com.palantir.logsafe.SafeArg")
.named("of");

private static final Matcher<ExpressionTree> TO_STRING =
MethodMatchers.instanceMethod().anyClass().named("toString").withNoParameters();

Expand Down Expand Up @@ -90,13 +95,25 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
.setMessage(String.format(
"Dangerous argument value: arg is '%s' but the parameter requires '%s'.",
argumentSafety, parameterSafety))
.addFix(getSuggestedFix(tree, state, argumentSafety))
tpetracca marked this conversation as resolved.
Show resolved Hide resolved
.build());
}
}
}
return Description.NO_MATCH;
}

private static SuggestedFix getSuggestedFix(MethodInvocationTree tree, VisitorState state, Safety argumentSafety) {
if (SAFE_ARG_OF_METHOD_MATCHER.matches(tree, state) && Safety.UNSAFE.allowsValueWith(argumentSafety)) {
SuggestedFix.Builder fix = SuggestedFix.builder();
String unsafeQualifiedClassName = SuggestedFixes.qualifyType(state, fix, UNSAFE_ARG);
String replacement = String.format("%s.of", unsafeQualifiedClassName);
return fix.replace(tree.getMethodSelect(), replacement).build();
}

return SuggestedFix.emptyFix();
}

private static Safety getSafety(ExpressionTree tree, VisitorState state) {
Tree argument = ASTHelpers.stripParentheses(tree);
// Check annotations on the result type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,75 @@ public void testIncomingParameter_toStringUsesObjectSafety() {
.doTest();
}

@Test
public void testSafeArgOfUnsafe_recommendsUnsafeArgOf() {
refactoringHelper()
.addInputLines(
"Test.java",
"import com.palantir.logsafe.SafeArg;",
"import com.palantir.logsafe.Unsafe;",
"class Test {",
" @Unsafe static class UnsafeClass {}",
" void f() {",
" SafeArg.of(\"unsafe\", new UnsafeClass());",
" }",
"}")
.addOutputLines(
"Test.java",
"import com.palantir.logsafe.SafeArg;",
"import com.palantir.logsafe.Unsafe;",
"import com.palantir.logsafe.UnsafeArg;",
"class Test {",
" @Unsafe static class UnsafeClass {}",
" void f() {",
" UnsafeArg.of(\"unsafe\", new UnsafeClass());",
" }",
"}")
.doTest();
}

@Test
public void testSafeArgOfDoNotLog_noRecommendation() {
refactoringHelper()
.addInputLines(
"Test.java",
"import com.palantir.logsafe.SafeArg;",
"import com.palantir.logsafe.DoNotLog;",
"class Test {",
" @DoNotLog static class DoNotLogClass {}",
" void f() {",
" // BUG: Diagnostic contains: Dangerous argument value: arg is 'DO_NOT_LOG'",
" SafeArg.of(\"unsafe\", new DoNotLogClass());",
" }",
"}")
.expectUnchanged()
.doTest();
}

@Test
public void testUnsafeArgumentForSafeParameter_noRecommendation() {
refactoringHelper()
.addInputLines(
"Test.java",
"import com.palantir.logsafe.Safe;",
"import com.palantir.logsafe.Unsafe;",
"class Test {",
" @Unsafe static class UnsafeClass {}",
" void f() {",
" // BUG: Diagnostic contains: Dangerous argument value: arg is 'UNSAFE'",
" fun(new UnsafeClass());",
" }",
" private static void fun(@Safe Object obj) {}",
"}")
.expectUnchanged()
.doTest();
}

private CompilationTestHelper helper() {
return CompilationTestHelper.newInstance(IllegalSafeLoggingArgument.class, getClass());
}

private RefactoringValidator refactoringHelper() {
return RefactoringValidator.of(IllegalSafeLoggingArgument.class, getClass());
}
}
5 changes: 5 additions & 0 deletions changelog/@unreleased/pr-2133.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: improvement
improvement:
description: suggested fixes for IllegalSafeLoggingArgument check
links:
- https://github.com/palantir/gradle-baseline/pull/2133
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class BaselineErrorProneExtension {
"ExecutorSubmitRunnableFutureIgnored",
"ExtendsErrorOrThrowable",
"FinalClass",
"IllegalSafeLoggingArgument",
"ImmutablesStyle",
"ImplicitPublicBuilderConstructor",
"JavaTimeDefaultTimeZone",
Expand Down