|
20 | 20 | import java.util.HashMap; |
21 | 21 | import java.util.List; |
22 | 22 | import java.util.Map; |
| 23 | +import javax.annotation.Nullable; |
23 | 24 | import org.sonar.check.Rule; |
24 | 25 | import org.sonar.plugins.python.api.PythonSubscriptionCheck; |
25 | 26 | import org.sonar.plugins.python.api.SubscriptionContext; |
26 | 27 | import org.sonar.plugins.python.api.tree.BaseTreeVisitor; |
27 | 28 | import org.sonar.plugins.python.api.tree.CallExpression; |
| 29 | +import org.sonar.plugins.python.api.tree.ClassDef; |
28 | 30 | import org.sonar.plugins.python.api.tree.FileInput; |
29 | 31 | import org.sonar.plugins.python.api.tree.ImportFrom; |
30 | 32 | import org.sonar.plugins.python.api.tree.Name; |
31 | 33 | import org.sonar.plugins.python.api.tree.Tree; |
| 34 | +import org.sonar.plugins.python.api.tree.TypeParams; |
| 35 | +import org.sonar.python.tree.TreeUtils; |
32 | 36 |
|
33 | 37 | @Rule(key = "S5953") |
34 | 38 | public class UndefinedSymbolsCheck extends PythonSubscriptionCheck { |
@@ -68,11 +72,29 @@ private static class UnresolvedSymbolsVisitor extends BaseTreeVisitor { |
68 | 72 |
|
69 | 73 | @Override |
70 | 74 | public void visitName(Name name) { |
71 | | - if (name.isVariable() && name.symbol() == null && !name.name().startsWith("_")) { |
| 75 | + if (name.isVariable() && name.symbol() == null && !name.name().startsWith("_") && !isTypeVar(name)) { |
72 | 76 | nameIssues.computeIfAbsent(name.name(), k -> new ArrayList<>()).add(name); |
73 | 77 | } |
74 | 78 | } |
75 | 79 |
|
| 80 | + private static boolean isTypeVar(Name name) { |
| 81 | + return TreeUtils.firstAncestor(name, tree -> classWithTypeVar(tree, name)) != null; |
| 82 | + } |
| 83 | + |
| 84 | + private static boolean classWithTypeVar(Tree tree, Name name) { |
| 85 | + if (tree instanceof ClassDef classDef) { |
| 86 | + return hasTypeVar(classDef.typeParams(), name); |
| 87 | + } |
| 88 | + return false; |
| 89 | + } |
| 90 | + |
| 91 | + private static boolean hasTypeVar(@Nullable TypeParams typeParams, Name name) { |
| 92 | + if (typeParams != null && !typeParams.typeParamsList().isEmpty()) { |
| 93 | + return typeParams.typeParamsList().stream().anyMatch(typeParam -> typeParam.name().name().equals(name.name())); |
| 94 | + } |
| 95 | + return false; |
| 96 | + } |
| 97 | + |
76 | 98 | @Override |
77 | 99 | public void visitImportFrom(ImportFrom importFrom) { |
78 | 100 | hasWildcardImport |= importFrom.wildcard() != null; |
|
0 commit comments