Skip to content

Commit cc06a21

Browse files
SONARPY-591 Ensure no FP for metaclasses in S3862 (SonarSource#799)
* SONARPY-591 Ensure no FP for metaclass in S3862 * Remove ClassSymbol#hasMetaClass API
1 parent 35a5328 commit cc06a21

6 files changed

Lines changed: 16 additions & 11 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.sonar.plugins.python.api.tree.YieldStatement;
4343
import org.sonar.plugins.python.api.types.InferredType;
4444
import org.sonar.python.api.PythonPunctuator;
45+
import org.sonar.python.semantic.ClassSymbolImpl;
4546
import org.sonar.python.types.InferredTypes;
4647

4748
@Rule(key = "S3862")
@@ -160,7 +161,7 @@ private static boolean isValidIterable(Expression expression, List<LocationInFil
160161
if (symbol.is(Symbol.Kind.CLASS)) {
161162
secondaries.add(((ClassSymbol) symbol).definitionLocation());
162163
// Metaclasses might add the method by default
163-
return ((ClassSymbol) symbol).hasMetaClass();
164+
return ((ClassSymbolImpl) symbol).hasSuperClassWithUnknownMetaClass();
164165
}
165166
}
166167
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,11 @@ def collections_no_issue():
234234
a, *rest = Counter(['a', 'b'])
235235
a, *rest = OrderedDict.fromkeys('abc')
236236
a, *rest = defaultdict(int, {0:0})
237+
238+
def inherits_from_metaclassed():
239+
import enum
240+
class MyEnum(enum.Enum):
241+
first = 0
242+
second = 1
243+
244+
for elem in MyEnum: ... # OK

python-frontend/src/main/java/org/sonar/plugins/python/api/symbols/ClassSymbol.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ public interface ClassSymbol extends Symbol {
3939
@Beta
4040
Optional<Symbol> resolveMember(String memberName);
4141

42-
@Beta
43-
boolean hasMetaClass();
44-
4542
@Beta
4643
boolean canHaveMember(String memberName);
4744

python-frontend/src/main/java/org/sonar/python/semantic/ClassSymbolImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ public Optional<Symbol> resolveMember(String memberName) {
157157
return Optional.empty();
158158
}
159159

160-
@Override
161160
public boolean hasMetaClass() {
162161
return hasMetaClass || membersByName().get("__metaclass__") != null;
163162
}
@@ -179,7 +178,7 @@ public boolean canHaveMember(String memberName) {
179178
return false;
180179
}
181180

182-
private boolean hasSuperClassWithUnknownMetaClass() {
181+
public boolean hasSuperClassWithUnknownMetaClass() {
183182
for (Symbol symbol : allSuperClasses(true)) {
184183
if (symbol.is(Kind.CLASS)) {
185184
ClassSymbolImpl superClass = (ClassSymbolImpl) symbol;

python-frontend/src/test/java/org/sonar/python/semantic/ClassSymbolTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ public void defines_metaclass() {
241241
Symbol symbol = classDef.name().symbol();
242242
assertThat(symbol).isInstanceOf(ClassSymbol.class);
243243
assertThat(symbol.kind()).isEqualTo(Symbol.Kind.CLASS);
244-
ClassSymbol classSymbol = (ClassSymbol) symbol;
244+
ClassSymbolImpl classSymbol = (ClassSymbolImpl) symbol;
245245
assertThat(classSymbol.hasUnresolvedTypeHierarchy()).isFalse();
246246
assertThat(classSymbol.superClasses()).isEmpty();
247247
assertThat(classSymbol.hasMetaClass()).isTrue();
@@ -290,7 +290,7 @@ public void defines_metaclass_python_2() {
290290
Symbol symbol = classDef.name().symbol();
291291
assertThat(symbol).isInstanceOf(ClassSymbol.class);
292292
assertThat(symbol.kind()).isEqualTo(Symbol.Kind.CLASS);
293-
ClassSymbol classSymbol = (ClassSymbol) symbol;
293+
ClassSymbolImpl classSymbol = (ClassSymbolImpl) symbol;
294294
assertThat(classSymbol.hasUnresolvedTypeHierarchy()).isFalse();
295295
assertThat(classSymbol.superClasses()).isEmpty();
296296
assertThat(classSymbol.hasMetaClass()).isTrue();
@@ -308,7 +308,7 @@ public void defines_attrs() {
308308
Symbol symbol = classDef.name().symbol();
309309
assertThat(symbol).isInstanceOf(ClassSymbol.class);
310310
assertThat(symbol.kind()).isEqualTo(Symbol.Kind.CLASS);
311-
ClassSymbol classSymbol = (ClassSymbol) symbol;
311+
ClassSymbolImpl classSymbol = (ClassSymbolImpl) symbol;
312312
assertThat(classSymbol.hasUnresolvedTypeHierarchy()).isFalse();
313313
assertThat(classSymbol.superClasses()).hasSize(1);
314314
assertThat(classSymbol.superClasses()).extracting(Symbol::name).containsExactly("A");

python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ public void metaclass_in_imported_symbol() {
276276

277277
Symbol importedASymbol = tree.globalVariables().iterator().next();
278278
assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS);
279-
ClassSymbol classA = (ClassSymbol) importedASymbol;
279+
ClassSymbolImpl classA = (ClassSymbolImpl) importedASymbol;
280280
assertThat(classA.hasMetaClass()).isTrue();
281-
assertThat(((ClassSymbolImpl) classA).metaclassFQN()).isEqualTo("abc.ABCMeta");
281+
assertThat(classA.metaclassFQN()).isEqualTo("abc.ABCMeta");
282282
}
283283

284284
@Test

0 commit comments

Comments
 (0)