Skip to content

Commit 8552531

Browse files
Seppli11sonartech
authored andcommitted
SONARPY-3219 Global Variables should propagate to functions (#665)
GitOrigin-RevId: dd84f0f3cbcf4577ad7da7ca4bb0d6f08b78d90d
1 parent 4b8713a commit 8552531

File tree

24 files changed

+350
-200
lines changed

24 files changed

+350
-200
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@
3434
import org.sonar.plugins.python.api.tree.Token;
3535
import org.sonar.plugins.python.api.tree.Tree;
3636
import org.sonar.plugins.python.api.types.InferredType;
37+
import org.sonar.plugins.python.api.types.v2.PythonType;
3738
import org.sonar.python.tree.TreeUtils;
3839
import org.sonar.python.types.InferredTypes;
3940
import org.sonar.python.types.TypeShed;
40-
import org.sonar.plugins.python.api.types.v2.PythonType;
4141

4242
import static org.sonar.python.tree.TreeUtils.nameFromExpression;
4343
import static org.sonar.python.types.InferredTypes.containsDeclaredType;
@@ -211,7 +211,8 @@ protected boolean isExpectedTypeSource(SubscriptionContext ctx, PythonType calle
211211
@Override
212212
protected boolean isException(SubscriptionContext ctx, PythonType calleeType) {
213213
var isCoroutine = ctx.typeChecker().typeCheckBuilder().isInstanceOf("typing.Coroutine").check(calleeType) == TriBool.TRUE;
214-
return super.isException(ctx, calleeType) || isCoroutine;
214+
var isTypeVar = ctx.typeChecker().typeCheckBuilder().isInstanceOf("typing.TypeVar").check(calleeType) == TriBool.TRUE;
215+
return super.isException(ctx, calleeType) || isCoroutine || isTypeVar;
215216
}
216217
}
217218

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Set, FrozenSet, Union, Coroutine, Callable
1+
from typing import Set, FrozenSet, Union, Coroutine, Callable, TypeVar
22
import asyncio
33

44
def empty_union(x: Union['A', 'B']):
@@ -59,3 +59,11 @@ def local_function() -> int:
5959
def calling_local_function():
6060
x = local_function()
6161
x() # Noncompliant
62+
63+
T = TypeVar("T", bound=Callable)
64+
def calling_type_var(function: T):
65+
function()
66+
67+
T2 = TypeVar("T2", bound=int)
68+
def calling_type_var(function: T2):
69+
function() # FN: We don't preserve the bound type of the TypeVar

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,14 @@ def no_fp_aws_elasticloadbalancing():
8989

9090
print(app_listener)
9191
print(net_listener)
92+
93+
94+
def locally_defined_function():
95+
def foo() -> None:
96+
...
97+
98+
def bar() -> int:
99+
return 42
100+
101+
a = foo() # Noncompliant
102+
b = bar() # OK

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

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
*/
1717
package org.sonar.plugins.python.api.types.v2;
1818

19-
import java.util.ArrayList;
2019
import java.util.List;
2120
import java.util.Objects;
2221
import java.util.Optional;
2322
import org.sonar.api.Beta;
2423
import org.sonar.plugins.python.api.LocationInFile;
2524
import org.sonar.plugins.python.api.TriBool;
25+
import org.sonar.python.semantic.v2.TypeBuilder;
2626

2727
@Beta
2828
public final class ObjectType implements PythonType {
@@ -31,32 +31,13 @@ public final class ObjectType implements PythonType {
3131
private final List<Member> members;
3232
private final TypeSource typeSource;
3333

34-
public ObjectType(TypeWrapper typeWrapper, List<PythonType> attributes, List<Member> members, TypeSource typeSource) {
34+
private ObjectType(TypeWrapper typeWrapper, List<PythonType> attributes, List<Member> members, TypeSource typeSource) {
3535
this.typeWrapper = typeWrapper;
3636
this.attributes = attributes;
3737
this.members = members;
3838
this.typeSource = typeSource;
3939
}
4040

41-
public ObjectType(PythonType type, List<PythonType> attributes, List<Member> members, TypeSource typeSource) {
42-
this(TypeWrapper.of(type), attributes, members, typeSource);
43-
}
44-
45-
public ObjectType(PythonType type) {
46-
this(type, TypeSource.EXACT);
47-
}
48-
49-
public ObjectType(TypeWrapper typeWrapper) {
50-
this(typeWrapper, new ArrayList<>(), new ArrayList<>(), TypeSource.EXACT);
51-
}
52-
53-
public ObjectType(PythonType type, TypeSource typeSource) {
54-
this(type, new ArrayList<>(), new ArrayList<>(), typeSource);
55-
}
56-
57-
public ObjectType(PythonType type, List<PythonType> attributes, List<Member> members) {
58-
this(type, attributes, members, TypeSource.EXACT);
59-
}
6041

6142
@Override
6243
public Optional<String> displayName() {
@@ -147,4 +128,62 @@ public String toString() {
147128
"typeSource=" + typeSource + ']';
148129
}
149130

131+
public static class Builder implements TypeBuilder<ObjectType> {
132+
private TypeWrapper typeWrapper;
133+
private List<PythonType> attributes = List.of();
134+
private List<Member> members = List.of();
135+
private TypeSource typeSource = TypeSource.EXACT;
136+
137+
private Builder(TypeWrapper typeWrapper) {
138+
this.typeWrapper = typeWrapper;
139+
}
140+
141+
public static Builder fromTypeWrapper(TypeWrapper typeWrapper) {
142+
return new Builder(typeWrapper);
143+
}
144+
145+
public static Builder fromType(PythonType type) {
146+
if (type instanceof ObjectType objectType) {
147+
return new Builder(objectType.typeWrapper())
148+
.withAttributes(objectType.attributes())
149+
.withMembers(objectType.members())
150+
.withTypeSource(objectType.typeSource());
151+
}
152+
return new Builder(TypeWrapper.of(type));
153+
}
154+
155+
@Override
156+
public ObjectType build() {
157+
return new ObjectType(typeWrapper, attributes, members, typeSource);
158+
}
159+
160+
@Override
161+
public TypeBuilder<ObjectType> withDefinitionLocation(LocationInFile definitionLocation) {
162+
throw new IllegalStateException("Object type does not have definition location");
163+
}
164+
165+
public Builder withType(PythonType type) {
166+
this.typeWrapper = TypeWrapper.of(type);
167+
return this;
168+
}
169+
170+
public Builder withAttributes(List<PythonType> attributes) {
171+
this.attributes = attributes;
172+
return this;
173+
}
174+
175+
public Builder withMembers(List<Member> members) {
176+
this.members = members;
177+
return this;
178+
}
179+
180+
public Builder withTypeSource(TypeSource typeSource) {
181+
this.typeSource = typeSource;
182+
return this;
183+
}
184+
}
185+
186+
public static ObjectType fromType(PythonType type) {
187+
return Builder.fromType(type).build();
188+
}
150189
}

python-frontend/src/main/java/org/sonar/plugins/python/api/types/v2/SelfType.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ public static PythonType of(@Nullable PythonType type) {
8383
if (type instanceof ObjectType objectType) {
8484
PythonType innerType = objectType.unwrappedType();
8585
SelfType selfType = new SelfType(innerType);
86-
return new ObjectType(selfType, objectType.attributes(), objectType.members(), objectType.typeSource());
86+
return ObjectType.Builder.fromType(objectType)
87+
.withType(selfType)
88+
.build();
8789
}
8890

8991
if (type instanceof UnionType unionType) {

python-frontend/src/main/java/org/sonar/python/semantic/v2/ObjectTypeBuilder.java

Lines changed: 0 additions & 71 deletions
This file was deleted.

python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/ParameterConverter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
package org.sonar.python.semantic.v2.converter;
1818

1919
import java.util.Optional;
20-
import org.sonar.python.index.FunctionDescriptor;
21-
import org.sonar.python.types.v2.LazyTypeWrapper;
2220
import org.sonar.plugins.python.api.types.v2.ObjectType;
2321
import org.sonar.plugins.python.api.types.v2.ParameterV2;
2422
import org.sonar.plugins.python.api.types.v2.PythonType;
25-
import org.sonar.python.types.v2.SimpleTypeWrapper;
2623
import org.sonar.plugins.python.api.types.v2.TypeWrapper;
24+
import org.sonar.python.index.FunctionDescriptor;
25+
import org.sonar.python.types.v2.LazyTypeWrapper;
26+
import org.sonar.python.types.v2.SimpleTypeWrapper;
2727

2828
public class ParameterConverter {
2929

@@ -33,7 +33,7 @@ public ParameterV2 convert(ConversionContext ctx, FunctionDescriptor.Parameter p
3333
.map(lt -> (TypeWrapper) new LazyTypeWrapper(lt))
3434
.orElseGet(() -> new SimpleTypeWrapper(PythonType.UNKNOWN));
3535

36-
var type = new ObjectType(typeWrapper);
36+
var type = ObjectType.Builder.fromTypeWrapper(typeWrapper).build();
3737

3838
return new ParameterV2(parameter.name(),
3939
new SimpleTypeWrapper(type),

python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/VariableDescriptorToPythonTypeConverter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
*/
1717
package org.sonar.python.semantic.v2.converter;
1818

19-
import org.sonar.python.index.Descriptor;
20-
import org.sonar.python.index.VariableDescriptor;
2119
import org.sonar.plugins.python.api.types.v2.ObjectType;
2220
import org.sonar.plugins.python.api.types.v2.PythonType;
23-
import org.sonar.python.types.v2.SpecialFormType;
2421
import org.sonar.plugins.python.api.types.v2.TypeWrapper;
22+
import org.sonar.python.index.Descriptor;
23+
import org.sonar.python.index.VariableDescriptor;
24+
import org.sonar.python.types.v2.SpecialFormType;
2525

2626
public class VariableDescriptorToPythonTypeConverter implements DescriptorToPythonTypeConverter {
2727

@@ -37,7 +37,7 @@ public PythonType convert(ConversionContext ctx, VariableDescriptor from) {
3737
return new SpecialFormType(fullyQualifiedName);
3838
}
3939
TypeWrapper typeWrapper = ctx.lazyTypesContext().getOrCreateLazyTypeWrapper(annotatedType);
40-
return new ObjectType(typeWrapper);
40+
return ObjectType.Builder.fromTypeWrapper(typeWrapper).build();
4141
}
4242
return PythonType.UNKNOWN;
4343
}

0 commit comments

Comments
 (0)