-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Implement -Xlint:private-shadow, type-parameter-shadow #17622
Implement -Xlint:private-shadow, type-parameter-shadow #17622
Conversation
Respectively warn about : - a private field or a class parameter that shadows a superclass field - a local type parameter that shadows a type already in the scope Fixes : scala#17612 and scala#17613
2c408fd
to
e5fd477
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.
Here is my review; sorry it took a bit long. I just came back from vacation.
Hi @szymon-rd, thanks for the review ! But there is the requested changes, let me know if it is convenient for you now. :) |
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.
Looks good! I have some small comments, then we can merge it.
@@ -717,8 +730,7 @@ object CheckUnused: | |||
|
|||
/** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ | |||
private def isOverriden(using Context): Boolean = | |||
sym.is(Flags.Override) || | |||
(sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) | |||
sym.is(Flags.Override) || lookForInheritedDecl(sym).isDefined |
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.
Is there a test covering a case for which this check had to be modified?
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.
That modification has been necessary to overcome a new non handled exception.
That exception appeared when putting the CheckUnused
and CheckShadowing
phases in the same MegaPhase
.
In facts when doing the same traversing for both phases, it seems that some Trees becomes empty values along with their symbols.
That work around solved all the test covering cases that appears in the CommunityBuildTestA
and CommunityBuildTestC
. One of them that I remembered exactly is the cask project.
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.
That's so strange... I'd rather we knew about the cause, but we should be all right if tests and community builds pass.
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, I never knew the exact cause honestly. During debugging, I simply saw that my way to look after inherited symbols with the same name (in CheckShadowing
's lookForShadowedField()
method) was more robust for handling the possible exceptions when reaching an empty Trees
.
Then for the global functionality, I indeed relied on the existent test cases.
Great, thank you. :) |
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.
Ok, so let's roll back to two separate megaphases and we should be good to go. (sorry for going back and forth with these phases)
@@ -717,8 +730,7 @@ object CheckUnused: | |||
|
|||
/** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ | |||
private def isOverriden(using Context): Boolean = | |||
sym.is(Flags.Override) || | |||
(sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) | |||
sym.is(Flags.Override) || lookForInheritedDecl(sym).isDefined |
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.
That's so strange... I'd rather we knew about the cause, but we should be all right if tests and community builds pass.
OK, it's done. Would you like that I open a new issue for that or you will manage it yourself ? |
It's okay as it is, as more linting phases will come we will try to put them together. |
@@ -33,6 +33,7 @@ class CompilationTests { | |||
compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init")), | |||
compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), | |||
compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), | |||
compileFilesInDir("tests/pos", defaultOptions.and("-Xlint:private-shadow", "-Xlint:type-parameter-shadow")), |
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.
Careful adding lines like this (#18683).
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.
We did it to avoid suffering from the problems we encountered with WUnused. We need to run a big code base with the linting flags enabled, and linting is a piece of the compiler that relies heavily on Typer. We've already seen that some minor details may cause crashes. However, I think it can be done e.g. with community build.
@@ -35,7 +35,8 @@ class Compiler { | |||
protected def frontendPhases: List[List[Phase]] = | |||
List(new Parser) :: // Compiler frontend: scanner, parser | |||
List(new TyperPhase) :: // Compiler frontend: namer, typer | |||
List(new CheckUnused.PostTyper) :: // Check for unused elements | |||
List(new CheckUnused.PostTyper) :: // Check for unused elements | |||
List(new CheckShadowing) :: // Check shadowing elements |
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.
Surely we can merge this miniphase with other mini phases?
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, that's the plan for linting. But a weird bug appeared when we did that, and we could not diagnose and fix it in a short time frame. I've planned to investigate that, but this version was tested, and we decided to go iteratively while not blocking this feature from releasing.
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.
Especially given that a megaphase seems to be a no-op when it is not enabled (isRunnable == false) - it does not introduce much cost.
(https://github.com/lampepfl/dotty/blob/main/compiler/src/dotty/tools/dotc/Run.scala#L242)
We should not have merged this PR. Making the tests take twice as long is a productivity drain for everybody else working on the project. Also I think simply creating a new mega phase should not be done without careful deliberation and benchmarking. Saying we will merge them later is no justification; main should always be the best we can make. If there is a temporary sub-optimal, one should collaborate on a branch, not on main. |
We need these tests to be run in the CI - Linting is a piece of the compiler that relies heavily on Typer. We've already seen that some minor details may cause crashes. It was the case with WUnused, and even in this issue - adding these tests helped prevent a bug. However, we probably should run them only on CI, and I agree that it would be better if they were not run locally on testCompilation.
We decided to merge this as a separate MegaPhase due to a bug that we had some trouble diagnosing in a short time frame. That way we were able to deliver this feature timely and, as far I know - without introducing a performance overhead (this phase is enabled by a flag and is seems to be no-op when the flag is false). We did a similar thing for WUnused, introducing even two MegaPhases to avoid false positives.
I see the value in implementing some features iteratively, especially when it does not introduce performance overhead. It also helps to deliver the updates fast. But I agree that it is something we can discuss in terms of our process.
We have planned this feature as part of the linting effort. It was present in Scala 2. Implementing it required adding some logic. However, it is not coupled with other parts, so I would argue that it will not significantly affect the experience of maintainers. |
Implemented two additional lint warnings for my bachelor project supervised by @anatoliykmetyuk.
Most of the work has been done in a new
MiniPhase
class calledCheckShadowing
and running in the same mega phase as theCheckUnused.PostTyper
mini phase.They respectively warn about :
Some examples can be seen in #17612 and #17613.