Skip to content
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

Merged
merged 13 commits into from
Sep 29, 2023

Conversation

schuetzcarl
Copy link
Contributor

Implemented two additional lint warnings for my bachelor project supervised by @anatoliykmetyuk.

Most of the work has been done in a new MiniPhase class called CheckShadowing and running in the same mega phase as the
CheckUnused.PostTyper mini phase.

They respectively warn about :

  • A private field or a class parameter that shadows a super-class field.
  • A type parameter that shadows a type already defined in the scope.

Some examples can be seen in #17612 and #17613.

@schuetzcarl schuetzcarl marked this pull request as draft May 30, 2023 23:59
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
@schuetzcarl schuetzcarl force-pushed the implement/Xlint/shadow_warnings branch from 2c408fd to e5fd477 Compare June 1, 2023 12:34
@schuetzcarl schuetzcarl marked this pull request as ready for review June 20, 2023 20:04
Copy link
Member

@szymon-rd szymon-rd left a 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.

@schuetzcarl
Copy link
Contributor Author

Here is my review; sorry it took a bit long. I just came back from vacation.

Hi @szymon-rd, thanks for the review !
No problem, it took also a bit long for me, I had not so much time in the second part of the summer.

But there is the requested changes, let me know if it is convenient for you now. :)

Copy link
Member

@szymon-rd szymon-rd left a 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.

compiler/src/dotty/tools/dotc/Compiler.scala Outdated Show resolved Hide resolved
@@ -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
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

@schuetzcarl
Copy link
Contributor Author

schuetzcarl commented Sep 27, 2023

Looks good! I have some small comments, then we can merge it.

Great, thank you. :)
I applied the last changes and I think there is only one remaining point about the compiler's pipeline, let me know which option do you prefer.

Copy link
Member

@szymon-rd szymon-rd left a 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
Copy link
Member

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.

compiler/src/dotty/tools/dotc/Compiler.scala Outdated Show resolved Hide resolved
@schuetzcarl
Copy link
Contributor Author

OK, it's done.

Would you like that I open a new issue for that or you will manage it yourself ?

@szymon-rd szymon-rd merged commit 6c37729 into scala:main Sep 29, 2023
18 checks passed
@szymon-rd
Copy link
Member

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")),
Copy link
Member

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).

Copy link
Member

@szymon-rd szymon-rd Oct 13, 2023

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
Copy link
Member

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?

Copy link
Member

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.

Copy link
Member

@szymon-rd szymon-rd Oct 13, 2023

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)

@odersky
Copy link
Contributor

odersky commented Oct 13, 2023

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.

odersky

This comment was marked as resolved.

@szymon-rd
Copy link
Member

szymon-rd commented Oct 13, 2023

@odersky

Making the tests take twice as long is a productivity drain for everybody else working on the project

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.

Also I think simply creating a new mega phase should not be done without careful deliberation and benchmarking.

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.

If there is a temporary sub-optimal, one should collaborate on a branch, not on main.

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.

I am also somewhat shocked by the size of this commit. The compiler is already large, adding loads of code for stuff like this will make harder for everyone else to deal with it.

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.

@Kordyjan Kordyjan added this to the 3.4.0 milestone Dec 20, 2023
WojciechMazur added a commit that referenced this pull request Jun 20, 2024
…LTS (#20628)

Backports #17622 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
WojciechMazur added a commit that referenced this pull request Jun 20, 2024
…LTS (#20685)

Backports #17622 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants