Skip to content

Commit c63eae2

Browse files
Fix quality issues (#1808)
1 parent 15fae70 commit c63eae2

18 files changed

+86
-80
lines changed

python-checks/src/test/java/org/sonar/python/checks/NonCallableCalledCheckTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package org.sonar.python.checks;
2121

22+
import java.util.List;
2223
import org.junit.jupiter.api.Test;
2324
import org.sonar.python.checks.utils.PythonCheckVerifier;
2425

@@ -29,4 +30,9 @@ void test() {
2930
PythonCheckVerifier.verify("src/test/resources/checks/nonCallableCalled.py", new NonCallableCalledCheck());
3031
}
3132

33+
@Test
34+
void test_multiple_files() {
35+
PythonCheckVerifier.verify(List.of("src/test/resources/checks/nonCallableCalledImporter.py", "src/test/resources/checks/nonCallableCalledImported.py"), new NonCallableCalledCheck());
36+
}
37+
3238
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class MyNonCallableClass:
2+
...
3+
4+
5+
class MyCallableClass:
6+
def __call__(self):
7+
...
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from nonCallableCalledImported import MyNonCallableClass, MyCallableClass
2+
3+
4+
imported_non_callable = MyNonCallableClass()
5+
imported_callable = MyCallableClass()
6+
7+
imported_non_callable() # Noncompliant
8+
imported_callable()
9+
10+
11+
class LocallyDefinedNonCallable:
12+
...
13+
14+
local_non_callable = LocallyDefinedNonCallable()
15+
local_non_callable() # Noncompliant

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public Tree root() {
5454
return rootTree;
5555
}
5656

57-
void addBindingUsage(Name nameTree, UsageV2.Kind kind, @Nullable String fullyQualifiedName) {
57+
void addBindingUsage(Name nameTree, UsageV2.Kind kind) {
5858
String symbolName = nameTree.name();
5959
if (!isExistingSymbol(symbolName)) {
6060
SymbolV2 symbol = new SymbolV2(symbolName);
@@ -90,7 +90,7 @@ void createSelfParameter(Parameter parameter) {
9090
return;
9191
}
9292
String symbolName = nameTree.name();
93-
//TODO: Check with SelfSymbolImpl
93+
//TODO: SONARPY-1865 Represent "self"
9494
SymbolV2 symbol = new SymbolV2(symbolName);
9595
symbols.add(symbol);
9696
symbolsByName.put(symbolName, symbol);

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,6 @@ private ModuleType createModuleFromSymbols(@Nullable String name, @Nullable Modu
9898
return module;
9999
}
100100

101-
private static PythonType convertToObjectType(Symbol symbol) {
102-
// What should we have here?
103-
return PythonType.UNKNOWN;
104-
}
105-
106101
private static PythonType convertToFunctionType(FunctionSymbol symbol, Map<Symbol, PythonType> createdTypesBySymbol) {
107102
if (createdTypesBySymbol.containsKey(symbol)) {
108103
return createdTypesBySymbol.get(symbol);
@@ -163,7 +158,8 @@ private PythonType convertToType(Symbol symbol, Map<Symbol, PythonType> createdT
163158
case CLASS -> convertToClassType((ClassSymbol) symbol, createdTypesBySymbol);
164159
case FUNCTION -> convertToFunctionType((FunctionSymbol) symbol, createdTypesBySymbol);
165160
case AMBIGUOUS -> convertToUnionType((AmbiguousSymbol) symbol, createdTypesBySymbol);
166-
case OTHER -> convertToObjectType(symbol);
161+
// Symbols that are neither classes or function nor ambiguous symbols whose alternatives are all classes or functions are considered of unknown type
162+
case OTHER -> PythonType.UNKNOWN;
167163
};
168164
}
169165

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public TypeInferenceV2(ProjectLevelTypeTable projectLevelTypeTable, PythonFile p
5555
}
5656

5757
public void inferTypes(FileInput fileInput) {
58-
TrivialTypeInferenceVisitor trivialTypeInferenceVisitor = new TrivialTypeInferenceVisitor(projectLevelTypeTable, pythonFile, symbolTable);
58+
TrivialTypeInferenceVisitor trivialTypeInferenceVisitor = new TrivialTypeInferenceVisitor(projectLevelTypeTable, pythonFile);
5959
fileInput.accept(trivialTypeInferenceVisitor);
6060

6161
inferTypesAndMemberAccessSymbols(fileInput);
@@ -88,7 +88,7 @@ private void inferTypesAndMemberAccessSymbols(FileInput fileInput) {
8888

8989
private void inferTypesAndMemberAccessSymbols(FunctionDef functionDef) {
9090
Set<Name> parameterNames = TreeUtils.nonTupleParameters(functionDef).stream()
91-
// TODO: it probably doesn't make sense to restrict to annotated parameters here
91+
// TODO SONARPY-1866: it probably doesn't make sense to restrict to annotated parameters here
9292
.filter(parameter -> parameter.typeAnnotation() != null)
9393
.map(Parameter::name)
9494
.collect(Collectors.toSet());

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

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public void visitPyListOrSetCompExpression(ComprehensionExpression tree) {
114114

115115
@Override
116116
public void visitFunctionDef(FunctionDef functionDef) {
117-
currentScope().addBindingUsage(functionDef.name(), UsageV2.Kind.FUNC_DECLARATION, null);
117+
currentScope().addBindingUsage(functionDef.name(), UsageV2.Kind.FUNC_DECLARATION);
118118
createAndEnterScope(functionDef, currentScope());
119119
createTypeParameters(functionDef.typeParams());
120120
createParameters(functionDef);
@@ -127,7 +127,7 @@ private void createTypeParameters(@Nullable TypeParams typeParams) {
127127
.map(TypeParams::typeParamsList)
128128
.stream()
129129
.flatMap(Collection::stream)
130-
.forEach(typeParam -> currentScope().addBindingUsage(typeParam.name(), UsageV2.Kind.TYPE_PARAM_DECLARATION, null));
130+
.forEach(typeParam -> currentScope().addBindingUsage(typeParam.name(), UsageV2.Kind.TYPE_PARAM_DECLARATION));
131131
}
132132

133133

@@ -151,7 +151,7 @@ private void createParameters(FunctionLike function) {
151151
.skip(hasSelf ? 1 : 0)
152152
.map(Parameter::name)
153153
.filter(Objects::nonNull)
154-
.forEach(param -> currentScope().addBindingUsage(param, UsageV2.Kind.PARAMETER, null));
154+
.forEach(param -> currentScope().addBindingUsage(param, UsageV2.Kind.PARAMETER));
155155

156156
parameterList.all().stream()
157157
.filter(param -> param.is(Tree.Kind.TUPLE_PARAMETER))
@@ -163,7 +163,7 @@ private void addTupleParamElementsToBindingUsage(TupleParameter param) {
163163
param.parameters().stream()
164164
.filter(p -> p.is(Tree.Kind.PARAMETER))
165165
.map(p -> ((Parameter) p).name())
166-
.forEach(name -> currentScope().addBindingUsage(name, UsageV2.Kind.PARAMETER, null));
166+
.forEach(name -> currentScope().addBindingUsage(name, UsageV2.Kind.PARAMETER));
167167
param.parameters().stream()
168168
.filter(p -> p.is(Tree.Kind.TUPLE_PARAMETER))
169169
.map(TupleParameter.class::cast)
@@ -172,13 +172,13 @@ private void addTupleParamElementsToBindingUsage(TupleParameter param) {
172172

173173
@Override
174174
public void visitTypeAliasStatement(TypeAliasStatement typeAliasStatement) {
175-
currentScope().addBindingUsage(typeAliasStatement.name(), UsageV2.Kind.TYPE_ALIAS_DECLARATION, null);
175+
currentScope().addBindingUsage(typeAliasStatement.name(), UsageV2.Kind.TYPE_ALIAS_DECLARATION);
176176
super.visitTypeAliasStatement(typeAliasStatement);
177177
}
178178

179179
@Override
180180
public void visitClassDef(ClassDef classDef) {
181-
currentScope().addBindingUsage(classDef.name(), UsageV2.Kind.CLASS_DECLARATION, null);
181+
currentScope().addBindingUsage(classDef.name(), UsageV2.Kind.CLASS_DECLARATION);
182182
createAndEnterScope(classDef, currentScope());
183183
createTypeParameters(classDef.typeParams());
184184
super.visitClassDef(classDef);
@@ -198,7 +198,7 @@ public void visitImportFrom(ImportFrom importFrom) {
198198
? moduleTree.names().stream().map(Name::name).collect(Collectors.joining("."))
199199
: null;
200200
if (importFrom.isWildcardImport()) {
201-
// FIXME: handle wildcard import
201+
// TODO: SONARPY-1781 handle wildcard import
202202
} else {
203203
createImportedNames(importFrom.importedNames(), moduleName, importFrom.dottedPrefixForModule());
204204
}
@@ -212,15 +212,15 @@ private void createImportedNames(List<AliasedName> importedNames, @Nullable Stri
212212
String targetModuleName = fromModuleName;
213213
Name alias = module.alias();
214214
if (targetModuleName != null) {
215-
addBindingUsage(alias == null ? nameTree : alias, UsageV2.Kind.IMPORT, null);
215+
addBindingUsage(alias == null ? nameTree : alias, UsageV2.Kind.IMPORT);
216216
} else if (alias != null) {
217-
addBindingUsage(alias, UsageV2.Kind.IMPORT, null);
217+
addBindingUsage(alias, UsageV2.Kind.IMPORT);
218218
} else if (dottedPrefix.isEmpty() && dottedNames.size() > 1) {
219219
// Submodule import
220-
addBindingUsage(nameTree, UsageV2.Kind.IMPORT, null);
220+
addBindingUsage(nameTree, UsageV2.Kind.IMPORT);
221221
} else {
222222
// It's a simple case - no "from" imports or aliasing
223-
addBindingUsage(nameTree, UsageV2.Kind.IMPORT, null);
223+
addBindingUsage(nameTree, UsageV2.Kind.IMPORT);
224224
}
225225
});
226226
}
@@ -238,12 +238,12 @@ public void visitComprehensionFor(ComprehensionFor tree) {
238238
}
239239

240240
private void addCompDeclarationParam(Tree tree) {
241-
boundNamesFromExpression(tree).forEach(name -> currentScope().addBindingUsage(name, UsageV2.Kind.COMP_DECLARATION, null));
241+
boundNamesFromExpression(tree).forEach(name -> currentScope().addBindingUsage(name, UsageV2.Kind.COMP_DECLARATION));
242242
}
243243

244244
private void createLoopVariables(ForStatement loopTree) {
245245
loopTree.expressions().forEach(expr ->
246-
boundNamesFromExpression(expr).forEach(name -> currentScope().addBindingUsage(name, UsageV2.Kind.LOOP_DECLARATION, null)));
246+
boundNamesFromExpression(expr).forEach(name -> currentScope().addBindingUsage(name, UsageV2.Kind.LOOP_DECLARATION)));
247247
}
248248

249249
@Override
@@ -260,7 +260,7 @@ public void visitAssignmentStatement(AssignmentStatement pyAssignmentStatementTr
260260
public void visitAnnotatedAssignment(AnnotatedAssignment annotatedAssignment) {
261261
if (annotatedAssignment.variable().is(Tree.Kind.NAME)) {
262262
Name variable = (Name) annotatedAssignment.variable();
263-
addBindingUsage(variable, UsageV2.Kind.ASSIGNMENT_LHS, null);
263+
addBindingUsage(variable, UsageV2.Kind.ASSIGNMENT_LHS);
264264
}
265265
super.visitAnnotatedAssignment(annotatedAssignment);
266266
}
@@ -283,7 +283,7 @@ public void visitAssignmentExpression(AssignmentExpression assignmentExpression)
283283
public void visitGlobalStatement(GlobalStatement globalStatement) {
284284
// Global statements are not binding usages, but we consider them as such for symbol creation
285285
globalStatement.variables().forEach(name -> {
286-
moduleScope.addBindingUsage(name, UsageV2.Kind.GLOBAL_DECLARATION, null);
286+
moduleScope.addBindingUsage(name, UsageV2.Kind.GLOBAL_DECLARATION);
287287
currentScope().addGlobalName(name);
288288
});
289289
super.visitGlobalStatement(globalStatement);
@@ -314,10 +314,6 @@ public void visitCapturePattern(CapturePattern capturePattern) {
314314
}
315315

316316
private void addBindingUsage(Name nameTree, UsageV2.Kind usage) {
317-
addBindingUsage(nameTree, usage, null);
318-
}
319-
320-
private void addBindingUsage(Name nameTree, UsageV2.Kind usage, @Nullable String fullyQualifiedName) {
321-
currentScope().addBindingUsage(nameTree, usage, fullyQualifiedName);
317+
currentScope().addBindingUsage(nameTree, usage);
322318
}
323319
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void updateProgramState(Tree element, ProgramState programState) {
8989
} else if (element instanceof FunctionDef functionDef) {
9090
handleDefinition(functionDef, state);
9191
} else {
92-
// TODO: isinstance visitor
92+
// Here we should run "isinstance" visitor when we handle declared types, to avoid FPs when type guard checks are made
9393
updateTree(element, state);
9494
}
9595
}

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import org.sonar.python.semantic.v2.ClassTypeBuilder;
5555
import org.sonar.python.semantic.v2.FunctionTypeBuilder;
5656
import org.sonar.python.semantic.v2.ProjectLevelTypeTable;
57-
import org.sonar.python.semantic.v2.SymbolTable;
5857
import org.sonar.python.semantic.v2.SymbolV2;
5958
import org.sonar.python.semantic.v2.UsageV2;
6059
import org.sonar.python.tree.DictionaryLiteralImpl;
@@ -79,14 +78,12 @@
7978
public class TrivialTypeInferenceVisitor extends BaseTreeVisitor {
8079

8180
private final ProjectLevelTypeTable projectLevelTypeTable;
82-
private final SymbolTable symbolTable;
8381
private final String fileId;
8482

8583
private final Deque<PythonType> typeStack = new ArrayDeque<>();
8684

87-
public TrivialTypeInferenceVisitor(ProjectLevelTypeTable projectLevelTypeTable, PythonFile pythonFile, SymbolTable symbolTable) {
85+
public TrivialTypeInferenceVisitor(ProjectLevelTypeTable projectLevelTypeTable, PythonFile pythonFile) {
8886
this.projectLevelTypeTable = projectLevelTypeTable;
89-
this.symbolTable = symbolTable;
9087
Path path = pathOf(pythonFile);
9188
this.fileId = path != null ? path.toString() : pythonFile.toString();
9289
}
@@ -101,7 +98,7 @@ public void visitFileInput(FileInput fileInput) {
10198
@Override
10299
public void visitStringLiteral(StringLiteral stringLiteral) {
103100
ModuleType builtins = this.projectLevelTypeTable.getModule();
104-
// TODO: multiple object types to represent str instance?
101+
// TODO: SONARPY-1867 multiple object types to represent str instance?
105102
PythonType strType = builtins.resolveMember("str").orElse(PythonType.UNKNOWN);
106103
((StringLiteralImpl) stringLiteral).typeV2(new ObjectType(strType, new ArrayList<>(), new ArrayList<>()));
107104
}
@@ -149,7 +146,7 @@ public void visitNumericLiteral(NumericLiteral numericLiteral) {
149146
@Override
150147
public void visitNone(NoneExpression noneExpression) {
151148
ModuleType builtins = this.projectLevelTypeTable.getModule();
152-
// TODO: multiple object types to represent str instance?
149+
// TODO: SONARPY-1867 multiple object types to represent str instance?
153150
PythonType noneType = builtins.resolveMember("NoneType").orElse(PythonType.UNKNOWN);
154151
((NoneExpressionImpl) noneExpression).typeV2(new ObjectType(noneType, new ArrayList<>(), new ArrayList<>()));
155152
}
@@ -159,7 +156,6 @@ public void visitListLiteral(ListLiteral listLiteral) {
159156
ModuleType builtins = this.projectLevelTypeTable.getModule();
160157
scan(listLiteral.elements());
161158
List<PythonType> pythonTypes = listLiteral.elements().expressions().stream().map(Expression::typeV2).distinct().toList();
162-
// TODO: cleanly reduce attributes
163159
PythonType listType = builtins.resolveMember("list").orElse(PythonType.UNKNOWN);
164160
((ListLiteralImpl) listLiteral).typeV2(new ObjectType(listType, pythonTypes, new ArrayList<>()));
165161
}
@@ -195,7 +191,7 @@ static void resolveTypeHierarchy(ClassDef classDef, ClassTypeBuilder classTypeBu
195191

196192
private static void addParentClass(ClassTypeBuilder classTypeBuilder, RegularArgument regularArgument) {
197193
Name keyword = regularArgument.keywordArgument();
198-
// TODO: store names if not resolved properly
194+
// TODO: SONARPY-1871 store names if not resolved properly
199195
if (keyword != null) {
200196
if ("metaclass".equals(keyword.name())) {
201197
PythonType argumentType = getTypeV2FromArgument(regularArgument);
@@ -205,7 +201,7 @@ private static void addParentClass(ClassTypeBuilder classTypeBuilder, RegularArg
205201
}
206202
PythonType argumentType = getTypeV2FromArgument(regularArgument);
207203
classTypeBuilder.superClasses().add(argumentType);
208-
// TODO: handle generics
204+
// TODO: SONARPY-1869 handle generics
209205
}
210206

211207
private static PythonType getTypeV2FromArgument(RegularArgument regularArgument) {
@@ -364,7 +360,7 @@ public void visitName(Name name) {
364360
.filter(Expression.class::isInstance)
365361
.map(Expression.class::cast)
366362
.map(Expression::typeV2)
367-
// FIXME: classes and functions should be propagated like other types
363+
// TODO: classes (SONARPY-1829) and functions should be propagated like other types
368364
.filter(t -> (t instanceof ClassType) || (t instanceof FunctionType))
369365
.ifPresent(type -> setTypeToName(name, type));
370366
}

python-frontend/src/main/java/org/sonar/python/tree/CallExpressionImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.sonar.python.types.HasTypeDependencies;
4848
import org.sonar.python.types.InferredTypes;
4949
import org.sonar.python.types.v2.ClassType;
50-
import org.sonar.python.types.v2.FunctionType;
5150
import org.sonar.python.types.v2.ObjectType;
5251
import org.sonar.python.types.v2.PythonType;
5352

0 commit comments

Comments
 (0)