Skip to content

Commit

Permalink
SONARPY-2204 propagate unary expression
Browse files Browse the repository at this point in the history
  • Loading branch information
Seppli11 committed Oct 23, 2024
1 parent bcd2b37 commit 774a2e6
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
* <p>
* 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
* <code>is</code>-comparison with such a reference will always return <code>False</code>.
* <p>
* Note that these are fully qualified names of (value-level) expressions, not types.
*/
private static final Set<String> FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF = new HashSet<>(
asList("frozenset", "bytes", "int", "float", "str", "tuple", "hash"));
private static final List<String> FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF =
asList("frozenset", "bytes", "int", "float", "str", "tuple", "hash");

/**
* Names of types that usually should not be compared with <code>is</code>.
* <p>
* Note that these are names of types, not `fqn`s of expressions.
*/
private static final Set<String> NAMES_OF_TYPES_UNSUITABLE_FOR_COMPARISON = new HashSet<>(
asList("frozenset", "bytes", "int", "float", "tuple"));
private static final List<String> NAMES_OF_TYPES_UNSUITABLE_FOR_COMPARISON =
asList("frozenset", "bytes", "int", "float", "tuple");

private TypeChecker typeChecker;
private TypeCheckBuilder isNoneTypeChecker;
Expand Down Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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<String, Kind> kindsByOperator() {
Map<String, Kind> map = new HashMap<>();
Expand Down Expand Up @@ -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;
}
}

0 comments on commit 774a2e6

Please sign in to comment.