Skip to content

Commit 4a197bb

Browse files
Add ClassSymbol#canHaveMember API
1 parent e6dd6dd commit 4a197bb

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

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

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

42+
@Beta
43+
boolean canHaveMember(String memberName);
44+
4245
@Beta
4346
boolean isOrExtends(String fullyQualifiedClassName);
4447

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,23 @@ public Optional<Symbol> resolveMember(String memberName) {
147147
return Optional.empty();
148148
}
149149

150+
@Override
151+
public boolean canHaveMember(String memberName) {
152+
if (hasUnresolvedTypeHierarchy()) {
153+
return true;
154+
}
155+
for (Symbol symbol : allSuperClasses(true)) {
156+
if (symbol.kind() == Kind.CLASS) {
157+
ClassSymbolImpl classSymbol = (ClassSymbolImpl) symbol;
158+
Symbol matchingMember = classSymbol.membersByName().get(memberName);
159+
if (matchingMember != null) {
160+
return true;
161+
}
162+
}
163+
}
164+
return false;
165+
}
166+
150167
@Override
151168
public LocationInFile definitionLocation() {
152169
return classDefinitionLocation;

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,39 @@ public void inherited_static_member() {
340340
"B.foo"
341341
);
342342

343+
assertThat(classSymbol.canHaveMember("foo")).isTrue();
343344
Symbol foo = classSymbol.resolveMember("foo").get();
344345
assertThat(foo.usages()).extracting(Usage::kind).containsExactly(Usage.Kind.ASSIGNMENT_LHS, Usage.Kind.OTHER);
345346
}
346347

348+
@Test
349+
public void inherits_from_ambiguous_symbol() {
350+
ClassSymbol classSymbol = lastClassSymbol(
351+
"if x:",
352+
" class A: ...",
353+
"else:",
354+
" class A:",
355+
" def foo(): ...",
356+
"class B(A): ..."
357+
);
358+
359+
assertThat(classSymbol.resolveMember("foo").isPresent()).isFalse();
360+
assertThat(classSymbol.canHaveMember("foo")).isTrue();
361+
}
362+
363+
@Test
364+
public void inherits_from_function_call() {
365+
ClassSymbol classSymbol = lastClassSymbol(
366+
"class A:",
367+
" def foo(): ...",
368+
"def func(): return A",
369+
"class B(func()): ..."
370+
);
371+
372+
assertThat(classSymbol.resolveMember("foo").isPresent()).isFalse();
373+
assertThat(classSymbol.canHaveMember("foo")).isTrue();
374+
}
375+
347376
private static void assertEqualsWithoutUsages(ClassSymbolImpl classSymbol) {
348377
ClassSymbolImpl copied = classSymbol.copyWithoutUsages();
349378
assertThat(copied.hasUnresolvedTypeHierarchy()).isEqualTo(classSymbol.hasUnresolvedTypeHierarchy());

0 commit comments

Comments
 (0)