2525import java .util .Optional ;
2626import java .util .Set ;
2727import java .util .stream .Collectors ;
28+ import javax .annotation .Nullable ;
2829import org .sonar .check .Rule ;
2930import org .sonar .plugins .python .api .PythonSubscriptionCheck ;
3031import org .sonar .plugins .python .api .SubscriptionContext ;
3132import org .sonar .plugins .python .api .quickfix .PythonQuickFix ;
32- import org .sonar .plugins .python .api .symbols .Symbol ;
3333import org .sonar .plugins .python .api .tree .Argument ;
3434import org .sonar .plugins .python .api .tree .CallExpression ;
35+ import org .sonar .plugins .python .api .tree .QualifiedExpression ;
3536import org .sonar .plugins .python .api .tree .RegularArgument ;
3637import org .sonar .plugins .python .api .tree .StringLiteral ;
3738import org .sonar .plugins .python .api .tree .Tree ;
39+ import org .sonar .plugins .python .api .types .v2 .matchers .TypeMatcher ;
40+ import org .sonar .plugins .python .api .types .v2 .matchers .TypeMatchers ;
3841import org .sonar .python .quickfix .TextEditUtils ;
3942import org .sonar .python .tree .TreeUtils ;
43+ import org .sonar .python .types .v2 .matchers .InternalTypeMatchers ;
4044
4145@ Rule (key = "S6735" )
4246public class PandasAddMergeParametersCheck extends PythonSubscriptionCheck {
@@ -102,12 +106,15 @@ int getArgumentPosition(String fullyQualifiedName) {
102106 private static final String DATAFRAME_JOIN_FQN = "pandas.core.frame.DataFrame.join" ;
103107 private static final String DATAFRAME_MERGE_FQN = "pandas.core.frame.DataFrame.merge" ;
104108 private static final String PANDAS_MERGE_FQN = "pandas.core.reshape.merge.merge" ;
109+ private static final TypeMatcher DATAFRAME_METHOD = TypeMatchers .isFunctionOwnerSatisfying (
110+ TypeMatchers .isType ("pandas.core.frame.DataFrame" ));
111+ private static final TypeMatcher DATAFRAME = TypeMatchers .isObjectOfType ("pandas.core.frame.DataFrame" );
112+ private static final TypeMatcher DATAFRAME_OR_UNION = TypeMatchers .any (
113+ DATAFRAME ,
114+ InternalTypeMatchers .isAnyTypeInUnionSatisfying (DATAFRAME ));
115+ private static final TypeMatcher PANDAS_MERGE = TypeMatchers .withFQN (PANDAS_MERGE_FQN );
105116
106117 private static final Set <Keywords > ON_KEYWORDS = EnumSet .of (Keywords .ON , Keywords .LEFT_ON , Keywords .RIGHT_ON );
107- private static final Set <String > METHODS = Set .of (
108- DATAFRAME_JOIN_FQN ,
109- DATAFRAME_MERGE_FQN ,
110- PANDAS_MERGE_FQN );
111118
112119 private static final Map <Integer , String > MESSAGES = Map .of (
113120 1 , "Specify the \" %s\" parameter of this %s." ,
@@ -123,10 +130,27 @@ public void initialize(Context context) {
123130
124131 private static void verifyMergeCallParameters (SubscriptionContext ctx ) {
125132 CallExpression callExpression = (CallExpression ) ctx .syntaxNode ();
126- Optional .ofNullable (callExpression .calleeSymbol ())
127- .map (Symbol ::fullyQualifiedName )
128- .filter (METHODS ::contains )
129- .ifPresent (fqn -> missingArguments (fqn , ctx , callExpression ));
133+ String fqn = mergeFunctionFqn (callExpression , ctx );
134+ if (fqn != null ) {
135+ missingArguments (fqn , ctx , callExpression );
136+ }
137+ }
138+
139+ @ Nullable
140+ private static String mergeFunctionFqn (CallExpression callExpression , SubscriptionContext ctx ) {
141+ if (callExpression .callee () instanceof QualifiedExpression qualifiedExpression
142+ && (DATAFRAME_METHOD .isTrueFor (callExpression .callee (), ctx )
143+ || DATAFRAME_OR_UNION .isTrueFor (qualifiedExpression .qualifier (), ctx ))) {
144+ return switch (qualifiedExpression .name ().name ()) {
145+ case "join" -> DATAFRAME_JOIN_FQN ;
146+ case "merge" -> DATAFRAME_MERGE_FQN ;
147+ default -> null ;
148+ };
149+ }
150+ if (PANDAS_MERGE .isTrueFor (callExpression .callee (), ctx )) {
151+ return PANDAS_MERGE_FQN ;
152+ }
153+ return null ;
130154 }
131155
132156 private static void missingArguments (String fullyQualifiedName , SubscriptionContext ctx , CallExpression callExpression ) {
0 commit comments