diff --git a/python-checks/src/main/java/org/sonar/python/checks/IdentityComparisonWithCachedTypesCheck.java b/python-checks/src/main/java/org/sonar/python/checks/IdentityComparisonWithCachedTypesCheck.java index 26fe377c67..1a026521cc 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/IdentityComparisonWithCachedTypesCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/IdentityComparisonWithCachedTypesCheck.java @@ -19,9 +19,7 @@ */ package org.sonar.python.checks; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.sonar.check.Rule; import org.sonar.plugins.python.api.PythonSubscriptionCheck; import org.sonar.plugins.python.api.SubscriptionContext; @@ -56,21 +54,21 @@ public class IdentityComparisonWithCachedTypesCheck extends PythonSubscriptionCh * Fully qualified names of constructors and functions that would are guaranteed to create fresh objects with * references not shared anywhere else. *

- * If a reference arises from an call to one of these functions, and it does not escape anywhere else, then an + * If a reference arises from a call to one of these functions, and it does not escape anywhere else, then an * is-comparison with such a reference will always return False. *

* Note that these are fully qualified names of (value-level) expressions, not types. */ - private static final Set FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF = new HashSet<>( - asList("frozenset", "bytes", "int", "float", "str", "tuple", "hash")); + private static final List FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF = + asList("frozenset", "bytes", "int", "float", "str", "tuple", "hash"); /** * Names of types that usually should not be compared with is. *

* Note that these are names of types, not `fqn`s of expressions. */ - private static final Set NAMES_OF_TYPES_UNSUITABLE_FOR_COMPARISON = new HashSet<>( - asList("frozenset", "bytes", "int", "float", "tuple")); + private static final List NAMES_OF_TYPES_UNSUITABLE_FOR_COMPARISON = + asList("frozenset", "bytes", "int", "float", "tuple"); private TypeChecker typeChecker; private TypeCheckBuilder isNoneTypeChecker; @@ -158,9 +156,9 @@ private boolean isUnsuitableType(PythonType type) { } private boolean isConstructorReturningUniqueRef(PythonType type) { - for(String constructorName : FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF) { + for (String constructorName : FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF) { TypeCheckBuilder constructorChecker = typeChecker.typeCheckBuilder().isTypeWithName(constructorName); - if(constructorChecker.check(type) == TriBool.TRUE) { + if (constructorChecker.check(type) == TriBool.TRUE) { return true; } } diff --git a/python-checks/src/test/resources/checks/identityComparisonWithCachedTypes.py b/python-checks/src/test/resources/checks/identityComparisonWithCachedTypes.py index 58823bea7f..621215fc09 100644 --- a/python-checks/src/test/resources/checks/identityComparisonWithCachedTypes.py +++ b/python-checks/src/test/resources/checks/identityComparisonWithCachedTypes.py @@ -18,6 +18,9 @@ def literal_comparison(param): (4, 5) is param # Noncompliant {{Replace this "is" operator with "=="; identity operator is not reliable here.}} # ^^ + param is -1 #Noncompliant + param is 1 + 1 #Noncompliant + def literal_comparison_compliant(param): param is param # ok from the point of this rule param is someUnknownFunction(42) # ok diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java b/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java index 8a4027928f..a9f0012f32 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java @@ -57,6 +57,7 @@ import org.sonar.plugins.python.api.tree.Tree; import org.sonar.plugins.python.api.tree.Tuple; import org.sonar.plugins.python.api.tree.TypeAnnotation; +import org.sonar.plugins.python.api.tree.UnaryExpression; import org.sonar.python.semantic.v2.ClassTypeBuilder; import org.sonar.python.semantic.v2.FunctionTypeBuilder; import org.sonar.python.semantic.v2.ProjectLevelTypeTable; @@ -72,6 +73,7 @@ import org.sonar.python.tree.SetLiteralImpl; import org.sonar.python.tree.StringLiteralImpl; import org.sonar.python.tree.TupleImpl; +import org.sonar.python.tree.UnaryExpressionImpl; import org.sonar.python.types.v2.ClassType; import org.sonar.python.types.v2.FunctionType; import org.sonar.python.types.v2.Member; @@ -152,6 +154,15 @@ public void visitNumericLiteral(NumericLiteral numericLiteral) { numericLiteralImpl.typeV2(new ObjectType(pythonType, new ArrayList<>(), new ArrayList<>())); } + @Override + public void visitUnaryExpression(UnaryExpression unaryExpr) { + super.visitUnaryExpression(unaryExpr); + + if(unaryExpr instanceof UnaryExpressionImpl unaryExprImpl) { + unaryExprImpl.typeV2(unaryExpr.expression().typeV2()); + } + } + @Override public void visitNone(NoneExpression noneExpression) { ModuleType builtins = this.projectLevelTypeTable.getBuiltinsModule(); diff --git a/python-frontend/src/main/java/org/sonar/python/tree/UnaryExpressionImpl.java b/python-frontend/src/main/java/org/sonar/python/tree/UnaryExpressionImpl.java index db001fbed0..dcb67968be 100644 --- a/python-frontend/src/main/java/org/sonar/python/tree/UnaryExpressionImpl.java +++ b/python-frontend/src/main/java/org/sonar/python/tree/UnaryExpressionImpl.java @@ -31,6 +31,7 @@ import org.sonar.plugins.python.api.tree.UnaryExpression; import org.sonar.plugins.python.api.types.InferredType; import org.sonar.python.types.InferredTypes; +import org.sonar.python.types.v2.PythonType; public class UnaryExpressionImpl extends PyTree implements UnaryExpression { @@ -39,6 +40,7 @@ public class UnaryExpressionImpl extends PyTree implements UnaryExpression { private final Kind kind; private final Token operator; private final Expression expression; + private PythonType type = PythonType.UNKNOWN; private static Map kindsByOperator() { Map map = new HashMap<>(); @@ -90,4 +92,14 @@ public InferredType type() { } return InferredTypes.anyType(); } + + public UnaryExpression typeV2(PythonType type) { + this.type = type; + return this; + } + + @Override + public PythonType typeV2() { + return type; + } }