2525import com .google .errorprone .BugPattern .StandardTags ;
2626import com .google .errorprone .VisitorState ;
2727import com .google .errorprone .bugpatterns .BugChecker .MethodInvocationTreeMatcher ;
28+ import com .google .errorprone .bugpatterns .threadsafety .ConstantExpressions ;
29+ import com .google .errorprone .bugpatterns .threadsafety .ConstantExpressions .ConstantExpressionVisitor ;
2830import com .google .errorprone .fixes .SuggestedFix ;
2931import com .google .errorprone .matchers .Description ;
3032import com .google .errorprone .matchers .Matcher ;
3133import com .google .errorprone .util .ASTHelpers ;
3234import com .sun .source .tree .ExpressionTree ;
3335import com .sun .source .tree .MethodInvocationTree ;
34- import java .util .regex .Pattern ;
36+ import com .sun .tools .javac .code .Symbol ;
37+ import com .sun .tools .javac .code .Symbol .MethodSymbol ;
38+ import java .util .concurrent .atomic .AtomicBoolean ;
39+ import javax .inject .Inject ;
3540
3641/**
3742 * Points out if Truth Library assert is called on a constant.
@@ -47,14 +52,19 @@ public class TruthConstantAsserts extends BugChecker implements MethodInvocation
4752 private static final Matcher <ExpressionTree > ASSERT_THAT =
4853 staticMethod ().onClass ("com.google.common.truth.Truth" ).named ("assertThat" );
4954
50- private static final Pattern EQ_NEQ = Pattern .compile ("isEqualTo|isNotEqualTo" );
51-
5255 private static final Matcher <ExpressionTree > TRUTH_SUBJECT_CALL =
5356 instanceMethod ()
5457 .onDescendantOf ("com.google.common.truth.Subject" )
55- .withNameMatching ( EQ_NEQ )
58+ .namedAnyOf ( "isEqualTo" , "isNotEqualTo" )
5659 .withParameters ("java.lang.Object" );
5760
61+ private final ConstantExpressions constantExpressions ;
62+
63+ @ Inject
64+ TruthConstantAsserts (ConstantExpressions constantExpressions ) {
65+ this .constantExpressions = constantExpressions ;
66+ }
67+
5868 @ Override
5969 public Description matchMethodInvocation (
6070 MethodInvocationTree methodInvocationTree , VisitorState state ) {
@@ -76,15 +86,35 @@ public Description matchMethodInvocation(
7686 return Description .NO_MATCH ;
7787 }
7888 // check that argument of assertThat is a constant
79- if (ASTHelpers . constValue (expr ) == null ) {
89+ if (! constantIsh (expr , state ) ) {
8090 return Description .NO_MATCH ;
8191 }
8292 // check that expectation isn't a constant
8393 ExpressionTree expectation = getOnlyElement (methodInvocationTree .getArguments ());
84- if (ASTHelpers . constValue (expectation ) != null ) {
94+ if (constantIsh (expectation , state ) ) {
8595 return Description .NO_MATCH ;
8696 }
8797 SuggestedFix fix = SuggestedFix .swap (expr , expectation , state );
8898 return describeMatch (methodInvocationTree , fix );
8999 }
100+
101+ private boolean constantIsh (ExpressionTree tree , VisitorState state ) {
102+ var constant = constantExpressions .constantExpression (tree , state ).orElse (null );
103+ if (constant == null ) {
104+ return false ;
105+ }
106+ // Identifiers can be considered constants, but they're exactly what we usually assert on! So
107+ // don't consider them to be constants in this context.
108+ AtomicBoolean involvesIdentifiers = new AtomicBoolean ();
109+ constant .accept (
110+ new ConstantExpressionVisitor () {
111+ @ Override
112+ public void visitIdentifier (Symbol identifier ) {
113+ if (!(identifier instanceof MethodSymbol )) {
114+ involvesIdentifiers .set (true );
115+ }
116+ }
117+ });
118+ return !involvesIdentifiers .get ();
119+ }
90120}
0 commit comments