2626import static java .util .Collections .unmodifiableList ;
2727
2828import com .google .common .collect .ArrayListMultimap ;
29- import com .google .common .collect .ListMultimap ;
3029import com .google .errorprone .BugPattern ;
3130import com .google .errorprone .VisitorState ;
3231import com .google .errorprone .bugpatterns .BugChecker .CompilationUnitTreeMatcher ;
33- import com .google .errorprone .bugpatterns .BugChecker .MethodInvocationTreeMatcher ;
34- import com .google .errorprone .bugpatterns .BugChecker .MethodTreeMatcher ;
3532import com .google .errorprone .matchers .Description ;
3633import com .google .errorprone .matchers .Matcher ;
3734import com .sun .source .tree .CompilationUnitTree ;
4138import com .sun .source .tree .MethodTree ;
4239import com .sun .source .tree .Tree ;
4340import com .sun .source .tree .VariableTree ;
41+ import com .sun .source .util .TreePathScanner ;
4442import com .sun .tools .javac .code .Symbol .MethodSymbol ;
4543import com .sun .tools .javac .code .Type ;
44+ import java .util .HashMap ;
4645import java .util .List ;
4746import java .util .Map ;
4847
7069 + "its caller's parameters, but its caller doesn't pass that parameter to it. It's "
7170 + "likely that it was intended to." )
7271public final class ChainingConstructorIgnoresParameter extends BugChecker
73- implements CompilationUnitTreeMatcher , MethodInvocationTreeMatcher , MethodTreeMatcher {
74- private final Map <MethodSymbol , List <VariableTree >> paramTypesForMethod = newHashMap ();
75- private final ListMultimap <MethodSymbol , Caller > callersToEvaluate = ArrayListMultimap .create ();
76-
72+ implements CompilationUnitTreeMatcher {
7773 @ Override
7874 public Description matchCompilationUnit (CompilationUnitTree tree , VisitorState state ) {
79- /*
80- * Clear the collections to save memory. (I wonder if it also helps to handle weird cases when a
81- * class has multiple definitions. But I would expect for multiple definitions within the same
82- * compiler invocation to cause deeper problems.)
83- */
84- paramTypesForMethod .clear ();
85- callersToEvaluate .clear (); // should have already been cleared
86- return NO_MATCH ;
87- }
75+ var paramTypesForMethod = new HashMap <MethodSymbol , List <VariableTree >>();
76+ var callersToEvaluate = ArrayListMultimap .<MethodSymbol , Caller >create ();
77+
78+ new TreePathScanner <Void , Void >() {
79+ @ Override
80+ public Void visitMethodInvocation (MethodInvocationTree tree , Void unused ) {
81+ // TODO(cpovirk): determine whether anyone might be calling Foo.this()
82+ if (isIdentifierWithName (tree .getMethodSelect (), "this" )) {
83+ var symbol = getSymbol (tree );
84+ callersToEvaluate .put (symbol , new Caller (tree , state .withPath (getCurrentPath ())));
85+ }
86+ return super .visitMethodInvocation (tree , null );
87+ }
8888
89- @ Override
90- public Description matchMethodInvocation (MethodInvocationTree tree , VisitorState state ) {
91- MethodSymbol symbol = getSymbol (tree );
92- // TODO(cpovirk): determine whether anyone might be calling Foo.this()
93- if (!isIdentifierWithName (tree .getMethodSelect (), "this" )) {
94- return NO_MATCH ;
95- }
96- callersToEvaluate .put (symbol , new Caller (tree , state ));
97- return evaluateCallers (symbol );
98- }
89+ @ Override
90+ public Void visitMethod (MethodTree tree , Void unused ) {
91+ var symbol = getSymbol (tree );
92+ if (symbol .isConstructor ()) {
93+ paramTypesForMethod .put (symbol , unmodifiableList (tree .getParameters ()));
94+ }
95+ return super .visitMethod (tree , null );
96+ }
97+ }.scan (tree , null );
9998
100- @ Override
101- public Description matchMethod (MethodTree tree , VisitorState state ) {
102- MethodSymbol symbol = getSymbol (tree );
103- if (!symbol .isConstructor ()) {
104- return NO_MATCH ;
99+ for (var symbol : callersToEvaluate .keySet ()) {
100+ evaluateCallers (paramTypesForMethod .get (symbol ), callersToEvaluate .get (symbol ));
105101 }
106- paramTypesForMethod .put (symbol , unmodifiableList (tree .getParameters ()));
107- return evaluateCallers (symbol );
108- }
109102
110- private Description evaluateCallers (MethodSymbol symbol ) {
111- List <VariableTree > paramTypes = paramTypesForMethod .get (symbol );
112- if (paramTypes == null ) {
113- // We haven't seen the declaration yet. We'll evaluate the call when we do.
114- return NO_MATCH ;
115- }
103+ // All matches are reported through reportMatch calls instead of return values.
104+ return NO_MATCH ;
105+ }
116106
117- for (Caller caller : callersToEvaluate .removeAll (symbol )) {
107+ private void evaluateCallers (List <VariableTree > paramTypes , List <Caller > callers ) {
108+ for (var caller : callers ) {
118109 VisitorState state = caller .state ;
119110 MethodInvocationTree invocation = caller .tree ;
120111
112+ if (isSuppressed (state )) {
113+ continue ;
114+ }
115+
121116 MethodTree callerConstructor = state .findEnclosing (MethodTree .class );
122117 if (callerConstructor == null ) {
123118 continue ; // impossible, at least in compilable code?
@@ -175,9 +170,6 @@ private Description evaluateCallers(MethodSymbol symbol) {
175170 */
176171 }
177172 }
178-
179- // All matches are reported through reportMatch calls instead of return values.
180- return NO_MATCH ;
181173 }
182174
183175 private static Map <String , Type > indexTypeByName (List <? extends VariableTree > parameters ) {
@@ -188,6 +180,15 @@ private static Map<String, Type> indexTypeByName(List<? extends VariableTree> pa
188180 return result ;
189181 }
190182
183+ private boolean isSuppressed (VisitorState state ) {
184+ for (Tree tree : state .getPath ()) {
185+ if (isSuppressed (tree , state )) {
186+ return true ;
187+ }
188+ }
189+ return false ;
190+ }
191+
191192 private void reportMatch (
192193 Tree diagnosticPosition , VisitorState state , Tree toReplace , String replaceWith ) {
193194 state .reportMatch (describeMatch (diagnosticPosition , replace (toReplace , replaceWith )));
0 commit comments