-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SONARPY-2313 Fix FN on S6543 when a generic class is defined in another file #2162
Conversation
bdaac22
to
b49d901
Compare
@@ -1,6 +1,6 @@ | |||
from typing import Any | |||
|
|||
class ParentClass(object): | |||
class ParentClass[T](object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the issues were initially not raised due to ParentClass
not being a proper generic class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job, well done! Super clean implementation 💪
class MyImportedChild(SomeGeneric[T]): ... | ||
class MyImportedChild2(SomeGeneric): ... | ||
class MyImportedChild3(SomeGeneric[str]): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit I think it is usually clearer to give more explicit names to show the intent of the class you create even in tests.
class MyImportedChild(SomeGeneric[T]): ... | |
class MyImportedChild2(SomeGeneric): ... | |
class MyImportedChild3(SomeGeneric[str]): ... | |
class MyImportedGenericTypeVarChild(SomeGeneric[T]): ... | |
class MyImportedGenericChild(SomeGeneric): ... | |
class MyImportedConcreteChild(SomeGeneric[str]): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed MyImportedChild2
to MyImportedNonGenericChild
(negated) to be more explicit about the fact it's no longer a generic class.
String annotatedType = from.annotatedType(); | ||
if (annotatedType != null) { | ||
if ("typing._SpecialForm".equals(annotatedType) && fullyQualifiedName != null) { | ||
// Defensive null check on fullyQualifiedName: it should never be null for SpecialForm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
if (pythonType instanceof ClassType classType && classType.isGeneric()) { | ||
return TriBool.TRUE; | ||
} | ||
if (pythonType instanceof SpecialFormType specialFormType) { | ||
return "typing.Generic".equals(specialFormType.fullyQualifiedName()) ? TriBool.TRUE : TriBool.UNKNOWN; | ||
} | ||
if (pythonType instanceof UnknownType.UnresolvedImportType unresolvedImportType && "typing.Generic".equals(unresolvedImportType.importPath())) { | ||
return TriBool.TRUE; | ||
} | ||
return TriBool.UNKNOWN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function never returns false. Is it a conservative choice? For example, It seems paradoxical for ClassType
to have isGeneric
false and to return UNKNOWN
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's conservative. I think that isGeneric
in ClassType
should probably be a TriBool
in fact, as what I want to express is that isGeneric=true
means that it's for sure a generic class, and false
just means there's no sign that it is.
I don't think it should have an impact until we actually want to raise issues on explicitly non generic classes, where this heuristic will have to be thought about more deeply.
b23cce7
to
dd77158
Compare
fc68d75
to
b5f24b4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! It is better with this last commit in my opinion. Good job
from genericTypeWithoutArgumentImported import ( | ||
SomeGeneric, | ||
SomeGenericWithTypeParam, | ||
MyImportedGenericTypeVarChild, | ||
MyImportedNonGenericChild, | ||
MyImportedConcreteChild, | ||
SomeGenericIncorrectlyDefined | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may be?
from genericTypeWithoutArgumentImported import ( | |
SomeGeneric, | |
SomeGenericWithTypeParam, | |
MyImportedGenericTypeVarChild, | |
MyImportedNonGenericChild, | |
MyImportedConcreteChild, | |
SomeGenericIncorrectlyDefined | |
) | |
from genericTypeWithoutArgumentImported import * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since support of wildcard import is brittle at the moment, I don't want to add confusion to this test: I prefer to keep the explicit import.
98694cc
to
bf2b80c
Compare
…s hierarchy with generic classes
bf2b80c
to
f627c32
Compare
Quality Gate passedIssues Measures |
This PR introduces the notion of a
SpecialFormType
.This kind of type is a special typing internal type used to mark typing constructs (such as
typing.Union
,typing.Optional
, etc...).In
typing.pyi
, these constructs are defined as:Representing these constructs as instances of the
ClassType
SpecialForm
is problematic for two reasons:ObjectType
instances ofSpecialForm
. Disambiguating them would be difficult (relying onattributes
, which is initially not suited to store simple names).Therefore, I believe introducing this special kind of type is simpler to circumvent these problems.