Skip to content

Commit e7c6574

Browse files
SONARPY-1800 NonCallableCallCheck migration: Use the display name of the new type model (#1777)
1 parent 3326647 commit e7c6574

File tree

13 files changed

+182
-56
lines changed

13 files changed

+182
-56
lines changed

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

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.sonar.plugins.python.api.tree.Expression;
2828
import org.sonar.plugins.python.api.tree.Tree;
2929
import org.sonar.plugins.python.api.types.InferredType;
30-
import org.sonar.python.types.InferredTypes;
3130
import org.sonar.python.types.v2.PythonType;
3231
import org.sonar.python.types.v2.TriBool;
3332

@@ -43,10 +42,10 @@ public void initialize(Context context) {
4342
CallExpression callExpression = (CallExpression) ctx.syntaxNode();
4443
Expression callee = callExpression.callee();
4544
InferredType calleeType = callee.type();
46-
PythonType typeV2 = callee.typeV2();
47-
if (isNonCallableType(typeV2)) {
45+
PythonType type = callee.typeV2();
46+
if (isNonCallableType(type)) {
4847
String name = nameFromExpression(callee);
49-
PreciseIssue preciseIssue = ctx.addIssue(callee, message(calleeType, name));
48+
PreciseIssue preciseIssue = ctx.addIssue(callee, message(type, name));
5049
LocationInFile location = typeClassLocation(calleeType);
5150
if (location != null) {
5251
preciseIssue.secondary(location, "Definition.");
@@ -55,22 +54,20 @@ public void initialize(Context context) {
5554
});
5655
}
5756

58-
protected static String addTypeName(InferredType type) {
59-
String typeName = InferredTypes.typeName(type);
60-
if (typeName != null) {
61-
return " has type " + typeName + " and it";
62-
}
63-
return "";
57+
protected static String addTypeName(PythonType type) {
58+
return type.displayName()
59+
.map(d -> " has type " + d + " and it")
60+
.orElse("");
6461
}
6562

6663
public boolean isNonCallableType(PythonType type) {
6764
return type.hasMember("__call__") == TriBool.FALSE;
6865
}
6966

70-
public String message(InferredType calleeType, @Nullable String name) {
67+
public String message(PythonType typeV2, @Nullable String name) {
7168
if (name != null) {
72-
return String.format("Fix this call; \"%s\"%s is not callable.", name, addTypeName(calleeType));
69+
return "Fix this call; \"%s\"%s is not callable.".formatted(name, addTypeName(typeV2));
7370
}
74-
return String.format("Fix this call; this expression%s is not callable.", addTypeName(calleeType));
71+
return "Fix this call; this expression%s is not callable.".formatted(addTypeName(typeV2));
7572
}
7673
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ def call_noncallable(p):
4242
x = 'str'
4343
x() # FN: multiple assignment not handled
4444

45+
46+
def call_no_name():
47+
42() # Noncompliant {{Fix this call; this expression has type int and it is not callable.}}
48+
4549
def flow_sensitivity():
4650
my_var = "hello"
4751
my_var = 42

python-frontend/src/main/java/org/sonar/python/types/v2/ClassType.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@ public ClassType(String name, List<PythonType> attributes) {
4747
}
4848

4949
@Override
50-
public String displayName() {
50+
public Optional<String> displayName() {
51+
return Optional.of("type");
52+
}
53+
54+
@Override
55+
public Optional<String> instanceDisplayName() {
5156
var splits = name.split("\\.");
5257
if (splits.length > 0) {
53-
return splits[splits.length - 1];
58+
return Optional.of(splits[splits.length - 1]);
5459
}
55-
return name;
60+
return Optional.of(name);
5661
}
5762

5863
@Override

python-frontend/src/main/java/org/sonar/python/types/v2/FunctionType.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.util.List;
2323
import java.util.Objects;
24+
import java.util.Optional;
2425
import javax.annotation.Nullable;
2526

2627
/**
@@ -53,6 +54,11 @@ public boolean equals(Object o) {
5354
&& Objects.equals(parameters, that.parameters);
5455
}
5556

57+
@Override
58+
public Optional<String> displayName() {
59+
return Optional.of("Callable");
60+
}
61+
5662
@Override
5763
public int hashCode() {
5864
return Objects.hash(name, attributes, parameters, returnType, isAsynchronous, hasDecorators, isInstanceMethod, hasVariadicParameter);

python-frontend/src/main/java/org/sonar/python/types/v2/ObjectType.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public ObjectType(PythonType type) {
3131
}
3232

3333
@Override
34-
public String displayName() {
35-
return type.displayName();
34+
public Optional<String> displayName() {
35+
return type.instanceDisplayName();
3636
}
3737

3838
@Override

python-frontend/src/main/java/org/sonar/python/types/v2/PythonType.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ default String name() {
3131
return this.toString();
3232
}
3333

34-
default String displayName() {
35-
return this.toString();
34+
default Optional<String> displayName() {
35+
return Optional.empty();
36+
}
37+
38+
default Optional<String> instanceDisplayName() {
39+
return Optional.empty();
3640
}
3741

3842
default boolean isCompatibleWith(PythonType another) {

python-frontend/src/main/java/org/sonar/python/types/v2/UnionType.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@
1919
*/
2020
package org.sonar.python.types.v2;
2121

22+
import java.util.ArrayList;
2223
import java.util.List;
24+
import java.util.Optional;
2325

2426
public record UnionType(List<PythonType> candidates) implements PythonType {
2527
public UnionType() {
2628
this(List.of());
2729
}
2830

2931
@Override
30-
public String displayName() {
31-
var candidatesName = candidates.stream().map(PythonType::displayName).toList();
32-
return "Union[%s]".formatted(String.join(", ", candidatesName));
32+
public Optional<String> displayName() {
33+
List<String> candidateNames = new ArrayList<>();
34+
for (PythonType candidate : candidates) {
35+
Optional<String> s = candidate.displayName();
36+
s.ifPresent(candidateNames::add);
37+
if (s.isEmpty()) {
38+
return Optional.empty();
39+
}
40+
}
41+
return Optional.of("Union[%s]".formatted(String.join(", ", candidateNames)));
3342
}
3443

3544
@Override

python-frontend/src/main/java/org/sonar/python/types/v2/UnknownType.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,4 @@ public boolean isCompatibleWith(PythonType another) {
2727
return true;
2828
}
2929

30-
@Override
31-
public String displayName() {
32-
return "UnknownType";
33-
}
34-
3530
}

python-frontend/src/test/java/org/sonar/python/types/v2/ClassTypeTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ void no_parents() {
5757
assertThat(classType.hasMember("unknown")).isEqualTo(TriBool.UNKNOWN);
5858
assertThat(classType.instancesHaveMember("__call__")).isEqualTo(TriBool.FALSE);
5959
assertThat(classType.instancesHaveMember("unknown")).isEqualTo(TriBool.FALSE);
60+
61+
assertThat(classType.displayName()).contains("type");
62+
assertThat(classType.instanceDisplayName()).contains("C");
6063
}
6164

6265
@Test
@@ -579,6 +582,21 @@ void builder() {
579582
assertThat(classTypeBuilder.build()).isEqualTo(classType);
580583
}
581584

585+
@Test
586+
void displayName() {
587+
ClassType classType = new ClassType("...");
588+
assertThat(classType.instanceDisplayName()).contains("...");
589+
assertThat(classType.displayName()).contains("type");
590+
591+
classType = new ClassType("MyClass");
592+
assertThat(classType.instanceDisplayName()).contains("MyClass");
593+
assertThat(classType.displayName()).contains("type");
594+
595+
classType = new ClassType("mymod.MyClass");
596+
assertThat(classType.instanceDisplayName()).contains("MyClass");
597+
assertThat(classType.displayName()).contains("type");
598+
}
599+
582600
public static ClassType classType(String... code) {
583601
return classTypes(code).get(0);
584602
}

python-frontend/src/test/java/org/sonar/python/types/v2/FunctionTypeTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ void arity() {
4040
FunctionType functionType = functionType("def fn(): pass");
4141
assertThat(functionType.isAsynchronous()).isFalse();
4242
assertThat(functionType.parameters()).isEmpty();
43+
assertThat(functionType.displayName()).contains("Callable");
44+
assertThat(functionType.instanceDisplayName()).isEmpty();
4345

4446
functionType = functionType("async def fn(p1, p2, p3): pass");
4547
assertThat(functionType.parameters()).extracting(ParameterV2::name).containsExactly("p1", "p2", "p3");

0 commit comments

Comments
 (0)