Skip to content

Commit d428d3e

Browse files
joke1196sonartech
authored andcommitted
SONARPY-3344: S5727: Should not raise an issue when checking if kwargs is not None (#492)
GitOrigin-RevId: 73707d1d08a5eb36e98abdcbab6e71068c26b9fe
1 parent 2c0b455 commit d428d3e

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

python-checks/src/main/java/org/sonar/python/checks/ComparisonToNoneCheck.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@
1919
import org.sonar.check.Rule;
2020
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2121
import org.sonar.plugins.python.api.SubscriptionContext;
22+
import org.sonar.plugins.python.api.symbols.Symbol;
23+
import org.sonar.plugins.python.api.symbols.Usage;
2224
import org.sonar.plugins.python.api.tree.BinaryExpression;
25+
import org.sonar.plugins.python.api.tree.Expression;
2326
import org.sonar.plugins.python.api.tree.IsExpression;
27+
import org.sonar.plugins.python.api.tree.Parameter;
2428
import org.sonar.plugins.python.api.tree.Tree;
2529
import org.sonar.plugins.python.api.tree.Tree.Kind;
2630
import org.sonar.plugins.python.api.types.BuiltinTypes;
2731
import org.sonar.plugins.python.api.types.InferredType;
32+
import org.sonar.python.tree.TreeUtils;
2833

2934
import static org.sonar.python.checks.utils.CheckUtils.isNone;
3035

@@ -54,8 +59,12 @@ private static void checkEqualityComparison(SubscriptionContext ctx, BinaryExpre
5459
private static void checkIdentityComparison(SubscriptionContext ctx, IsExpression comparison) {
5560
InferredType left = comparison.leftOperand().type();
5661
InferredType right = comparison.rightOperand().type();
62+
5763
// `isObject` Removes FP when the return type of a function is an object
58-
if (!left.isIdentityComparableWith(right) && (isNone(left) || isNone(right)) && !(isObject(left) || isObject(right))) {
64+
// `isArgsOrKwargs` Removes FP when *args or **kwargs are used as their type are tuple and dict when type hinted with `Any`.
65+
var isIdentityComparableWith = left.isIdentityComparableWith(right);
66+
var isOperandArgsOrKwargs = isArgsOrKwargs(comparison.leftOperand()) || isArgsOrKwargs(comparison.rightOperand());
67+
if (!isIdentityComparableWith && (isNone(left) || isNone(right)) && !(isObject(left) || isObject(right)) && !isOperandArgsOrKwargs) {
5968
addIssue(ctx, comparison, "identity check", comparison.notToken() != null);
6069
} else if (isNone(left) && isNone(right)) {
6170
addIssue(ctx, comparison, "identity check", comparison.notToken() == null);
@@ -75,4 +84,12 @@ private static boolean isObject(InferredType type) {
7584
return type.canOnlyBe(BuiltinTypes.OBJECT_TYPE);
7685
}
7786

87+
private static boolean isArgsOrKwargs(Expression expr) {
88+
return TreeUtils.getSymbolFromTree(expr).map(Symbol::usages)
89+
.map(usages -> usages.stream().anyMatch(ComparisonToNoneCheck::isUsedAsArgsOrKwargs)).orElse(false);
90+
}
91+
92+
private static boolean isUsedAsArgsOrKwargs(Usage usage) {
93+
return usage.tree().parent() instanceof Parameter parameter && parameter.starToken() != null;
94+
}
7895
}

python-checks/src/test/resources/checks/comparisonToNoneCheck.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from typing import Any, Tuple, Dict
12
import pydoc
3+
24
def identity_check(param):
35
a = None
46
b = 42
@@ -50,3 +52,10 @@ def equality_check(param):
5052
if obj == None: pass # FN
5153
if None == obj: pass # FN
5254

55+
def kwargs_any(*args: Any, **some_dict:Any):
56+
if args is not None: ...
57+
if some_dict is not None: ...
58+
59+
def kwargs(*some_args: Any, **kwargs: Any):
60+
if kwargs is not None: ...
61+
if some_args is not None: ...

0 commit comments

Comments
 (0)