1919 */
2020package org .sonar .python .checks ;
2121
22+ import java .util .HashSet ;
2223import java .util .List ;
2324import java .util .Set ;
2425import java .util .stream .Collectors ;
@@ -45,16 +46,6 @@ public class ArgumentNumberCheck extends PythonSubscriptionCheck {
4546
4647 private static final String FUNCTION_DEFINITION = "Function definition." ;
4748
48- private static String message (String functionName , long minRequiredPositionalArguments , int nArguments , long nPositionalParamWithDefaultValue ) {
49- String message = "" ;
50- if (minRequiredPositionalArguments > nArguments ) {
51- message = "Add " + (minRequiredPositionalArguments - nArguments ) + " missing arguments; " ;
52- } else {
53- message = "Remove " + (nArguments - minRequiredPositionalArguments - nPositionalParamWithDefaultValue ) + " unexpected arguments; " ;
54- }
55- return message + "'" + functionName + "' expects " + minRequiredPositionalArguments + " positional arguments." ;
56- }
57-
5849 @ Override
5950 public void initialize (Context context ) {
6051 context .registerSyntaxNodeConsumer (Tree .Kind .CALL_EXPR , ctx -> {
@@ -65,29 +56,77 @@ public void initialize(Context context) {
6556 if (isException (callExpression , functionSymbol )) {
6657 return ;
6758 }
68- int nArguments = callExpression .arguments ().size ();
69- int self = 0 ;
70- if (functionSymbol .isInstanceMethod () && callExpression .callee ().is (Tree .Kind .QUALIFIED_EXPR )) {
71- self = 1 ;
72- }
73- long minRequiredPositionalArguments = functionSymbol .parameters ().stream ()
74- .filter (parameterName -> !parameterName .isKeywordOnly () && !parameterName .hasDefaultValue ()).count ();
75- if ((nArguments + self < minRequiredPositionalArguments || nArguments + self > functionSymbol .parameters ().size ())) {
76- PreciseIssue preciseIssue = ctx .addIssue (callExpression .callee (),
77- message (functionSymbol .name (), minRequiredPositionalArguments - self , nArguments , functionSymbol .parameters ().size () - minRequiredPositionalArguments ));
78- addSecondary (functionSymbol , preciseIssue );
79- }
80-
59+ checkPositionalParameters (ctx , callExpression , functionSymbol );
8160 checkKeywordArguments (ctx , callExpression , functionSymbol , callExpression .callee ());
8261 }
8362
8463 });
8564 }
8665
66+ private static void checkPositionalParameters (SubscriptionContext ctx , CallExpression callExpression , FunctionSymbol functionSymbol ) {
67+ int self = 0 ;
68+ if (functionSymbol .isInstanceMethod () && callExpression .callee ().is (Tree .Kind .QUALIFIED_EXPR )) {
69+ self = 1 ;
70+ }
71+ Set <String > positionalParamsWithoutDefault = positionalParamsWithoutDefault (functionSymbol );
72+ long nbPositionalParamsWithDefault = functionSymbol .parameters ().stream ()
73+ .filter (parameterName -> !parameterName .isKeywordOnly () && parameterName .hasDefaultValue ())
74+ .count ();
75+
76+ List <RegularArgument > arguments = callExpression .arguments ().stream ()
77+ .map (RegularArgument .class ::cast )
78+ .collect (Collectors .toList ());
79+ long nbPositionalArgs = arguments .stream ().filter (a -> a .keywordArgument () == null ).count ();
80+ long nbNonKeywordOnlyPassedWithKeyword = arguments .stream ()
81+ .map (RegularArgument ::keywordArgument )
82+ .filter (k -> k != null && positionalParamsWithoutDefault .contains (k .name ()))
83+ .count ();
84+
85+ int minimumPositionalArgs = positionalParamsWithoutDefault .size ();
86+ String expected = "" + (minimumPositionalArgs - self );
87+ long nbMissingArgs = minimumPositionalArgs - nbPositionalArgs - self - nbNonKeywordOnlyPassedWithKeyword ;
88+ if (nbMissingArgs > 0 ) {
89+ String message = "Add " + nbMissingArgs + " missing arguments; " ;
90+ if (nbPositionalParamsWithDefault > 0 ) {
91+ expected = "at least " + expected ;
92+ }
93+ addPositionalIssue (ctx , callExpression .callee (), functionSymbol , message , expected );
94+ } else if (nbMissingArgs + nbPositionalParamsWithDefault < 0 ) {
95+ String message = "Remove " + (- nbMissingArgs - nbPositionalParamsWithDefault ) + " unexpected arguments; " ;
96+ if (nbPositionalParamsWithDefault > 0 ) {
97+ expected = "at most " + (minimumPositionalArgs - self + nbPositionalParamsWithDefault );
98+ }
99+ addPositionalIssue (ctx , callExpression .callee (), functionSymbol , message , expected );
100+ }
101+ }
102+
103+ private static Set <String > positionalParamsWithoutDefault (FunctionSymbol functionSymbol ) {
104+ int unnamedIndex = 0 ;
105+ Set <String > result = new HashSet <>();
106+ for (FunctionSymbol .Parameter parameter : functionSymbol .parameters ()) {
107+ if (!parameter .isKeywordOnly () && !parameter .hasDefaultValue ()) {
108+ String name = parameter .name ();
109+ if (name == null ) {
110+ result .add ("!unnamed" + unnamedIndex );
111+ unnamedIndex ++;
112+ } else {
113+ result .add (name );
114+ }
115+ }
116+ }
117+ return result ;
118+ }
119+
120+ private static void addPositionalIssue (SubscriptionContext ctx , Tree tree , FunctionSymbol functionSymbol , String message , String expected ) {
121+ String msg = message + "'" + functionSymbol .name () + "' expects " + expected + " positional arguments." ;
122+ PreciseIssue preciseIssue = ctx .addIssue (tree , msg );
123+ addSecondary (functionSymbol , preciseIssue );
124+ }
125+
87126 private static boolean isReceiverClassSymbol (QualifiedExpression qualifiedExpression ) {
88127 return TreeUtils .getSymbolFromTree (qualifiedExpression .qualifier ())
89- .filter (symbol -> symbol .kind () == Symbol .Kind .CLASS )
90- .isPresent ();
128+ .filter (symbol -> symbol .kind () == Symbol .Kind .CLASS )
129+ .isPresent ();
91130 }
92131
93132 private static boolean isException (CallExpression callExpression , FunctionSymbol functionSymbol ) {
0 commit comments