-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Improve type resolution for duplicate names #3012
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
Improve type resolution for duplicate names #3012
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3012 +/- ##
==========================================
Coverage ? 57.357%
Complexity ? 2562
==========================================
Files ? 616
Lines ? 34280
Branches ? 5796
==========================================
Hits ? 19662
Misses ? 12658
Partials ? 1960
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
My opinion is that i don't think there is a need to create a new context (ClassOrInterfaceDeclarationExtendsContext) to implements this behavior. |
Do you prefer extending ClassOrInterfaceDeclarationContext with a final field to switch behavior (ie only include type parameters or everything) instead? |
I think you are right, ClassOrInterfaceDeclarationContext must verify inner classes before implemented or extended types. How it should it be done is another question. For the moment I have no answer because there are many different cases that may arise. |
Right. I tried a few different solutions that tried to reorder the lookup, or skip for specific case and similar. That's why I opted to add ClassOrInterfaceDeclarationExtendsContext to just search in the types that should be searched instead of trying to get ClassOrInterfaceDeclerationContext to return the right thing for extends and implements. |
I've not yet had time to verify the test fails before the fix then passes with it, or to otherwise look at it in detail, so don't have a strong view other way. Would it be reasonable to add this as-is and then iterate on it to improve/polish it later? |
It's okay for me. Maybe we should then create an issue to remember that we eventually need to review this implementation |
I found another solution that also fixes the issue #2995. I'm not sure it solves all cases and maybe it will need to be improved but it covers current issues. I will push a PR for these cases. |
a55a80e
to
2e927d8
Compare
Did a quick rebase on the fix for #2995. Wondering tho, is JavaParserTypeDeclarationAdapter meant to go away or why are fixes added to ClassOrInterfaceDeclarationContext instead of fixing the adapter? I moved the #2995 fix to the adapter as part of this PR because with the lookup in extends/implements before local types moved to after local types (as needed for type lookup inside the class) the context.solveType() mainly just duplicated what javaParserTypeDeclarationAdapter.solveType() call will do again anyway. |
I'm not sure but it looks like JavaParserTypeDeclarationAdapter is set up to factorize common behavior between contexts related to enums, annotations, and classes / interfaces. It seems to me that it makes sense to keep the ClassOrInterfaceDeclarationContext class which implements the specificities related to classes / interfaces. For example, to my knowledge, there is no inheritance on enumerations, so it is inappropriate to have notions of inheritance in a class used by enumerations. Likewise it does not seem to me that the notion of inheritance on annotations has the same meaning as the inheritance of classes / interfaces (it is more a notion of applicability). |
Ok, so JavaParserTypeDeclarationAdapter should only search common parts, I can see that but it looks like it leads to a fair amount of code duplication and it makes it trickier to keep the lookup priorities straight. Maybe it's just me but ending up with a ClassOrInterfaceDeclarationContext#solveType implementation that does (current master):
JavaParserTypeDeclarationAdapter#solveType: looks both errorprone and somewhat ineffective to me. But I'm new to this code, to get the right answer when resolving types inside a class I mainly just need to move (2) and (3) after (4) and add the limited context for when resolving implements and extends so if that is preferred I'll do that. |
I agree with your analysis. There are code duplications which are a potential source of bugs. I'm also not completely sure what the JavaParserTypeDeclarationAdapter class (versus ClassOrInterfaceDeclarationContext) should implement. Maybe @matozoid |
2e927d8
to
f51a6c8
Compare
f51a6c8
to
5b8ca8b
Compare
@thejk - are you in a position to look at the merge conflict? 👍 If not, I'll take a look next week. |
8884127
to
d9a080a
Compare
Sure, pushed a rebase. |
When resolving types inside a ClassOrInterfaceDeclarationContext the types found in extends and implements where searched before local members. This caused problems for code such as: import other.I; class A implements I { interface I { } void foo(I i) { } } In the above the first parameter type for "foo" should be A.I and not other.I. Moving the code in ClassOrInterfaceDeclarationContext that searched extends and implements first to JavaParserTypeDeclarationAdapter and after searching local members showed the original problem of using ClassOrInterfaceDeclarationContext when resolving extends and implements as now A would instead implement A.I. Made a new fix for that by introducing ClassOrInterfaceDeclarationExtendsContext which only seaches in the parent context with one exception: type parameters.
d9a080a
to
15174d9
Compare
I also agree that it needs some organisation. There are a few ways it could be done -- one is to continue using the adapter, another is to insert an To give an example, I imagine that these small granular methods be defined (in the adapter or the abstract class):
... then the context implementations would use those shared methods like this, making the search order explicit while also removing (some of the) duplication. @Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
Optional<SymbolReference<ResolvedTypeDeclaration>> solvedType;
solvedType = solveTypeInOwnName(name);
if(solvedType.isPresent()) {
return solvedType.get();
}
solvedType = solveNameInInterfaceList(name);
if(solvedType.isPresent()) {
return solvedType.get();
}
solvedType = solveNameInExtendsList(name);
if(solvedType.isPresent()) {
return solvedType.get();
}
solvedType = solveNameInAncestors(name);
if(solvedType.isPresent()) {
return solvedType.get();
}
// Symbol not found within this context
// Search within the parent context
return getParent()
.orElseThrow(() -> new RuntimeException("Parent context unexpectedly empty."))
.solveType(name);
} For now though, I'll merge this PR and any moves one way or another can be refactored within a new PR 👍 |
When resolving types inside a ClassOrInterfaceDeclarationContext the
types found in extends and implements where searched before local
members. This caused problems for code such as:
In the above the first parameter type for "foo" should be A.I and not
other.I.
Moving the code in ClassOrInterfaceDeclarationContext that searched
extends and implements first to JavaParserTypeDeclarationAdapter and
after searching local members showed the original problem of using
ClassOrInterfaceDeclarationContext when resolving extends and
implements as now A would instead implement A.I. Made a new fix for
that by introducing ClassOrInterfaceDeclarationExtendsContext which
only seaches in the parent context with one exception: type parameters.