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

Mention if missing class is on the class path #10774

Open
wants to merge 1 commit into
base: 2.13.x
Choose a base branch
from

Conversation

som-snytt
Copy link
Contributor

Hint if there is an object in scope, in a package, which has a class, and the class path finds it.

Fixes scala/bug#12289

@scala-jenkins scala-jenkins added this to the 2.13.15 milestone May 7, 2024
@som-snytt
Copy link
Contributor Author

java.io.IOException: No space left on device

@SethTisue
Copy link
Member

I re-ran the Jenkins build, now that the community build runs that were using all the disk have finished.

I wonder sometimes if we should have a dedicated worker behemoth just for PR validation, separate from the behemoths used for the community build.

@som-snytt
Copy link
Contributor Author

Needs refinement, as it does add verbiage for incremental compilation error.

[error] /home/amarki/snips/test-12289/src/main/scala/c.scala:4:17: not found: type T
[error] A definition on the class path may be hidden by a source artifact. (Companions must be defined together.)

because (as will surprise no one) the class is still there, i.e., previous artifacts are not cleaned first.

./target/scala-2.13/classes/p/Main.class
./target/scala-2.13/classes/p/T.class
./target/scala-2.13/classes/p/Main$.class
./target/scala-2.13/classes/p/C.class
./target/scala-2.13/classes/p/Main$delayedInit$body.class

Hint if there is an object in scope, in a package,
which has a class, and the class path finds it.
@som-snytt som-snytt force-pushed the issue/12289-module-class-shadowing branch from a41bb8c to f89150a Compare May 8, 2024 01:09
@som-snytt
Copy link
Contributor Author

som-snytt commented May 8, 2024

The clever test is tweaked to allow -d.

The output check in typer may require tweaking: it uses single output only, and compares the path strings to mean "includes".

@som-snytt som-snytt marked this pull request as ready for review May 8, 2024 01:12
Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a helpful addition. Given

package p {
  object T
  class C extends T
}

and

package p {
  trait T
}

I assume IntelliJ would happily navigate from extends T to the definition of trait T, which makes it harder to figure why compilation fails.

@@ -0,0 +1,6 @@
//> using options -d /tmp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make it a DirectTest and use testOutput.path?

case NoSymbol => issue(SymbolNotFoundError(tree, name, context.owner, startContext, mode.in(all = PATTERNmode, none = APPSELmode | TYPEPATmode)))
case NoSymbol =>
def hasOutput = settings.outputDirs.getSingleOutput.isDefined || settings.outputDirs.outputs.nonEmpty
val hidden = !unit.isJava && name.isTypeName && hasOutput && {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue doesn't manifest the other way around, if name.isTermName?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was too lazy to check after getting a first cut. Also, I considered it more likely to add an object as factory for an existing class.

val hidden = !unit.isJava && name.isTypeName && hasOutput && {
startContext.lookupSymbol(name.toTermName, _ => true) match {
case LookupSucceeded(qualifier, symbol) if symbol.owner.hasPackageFlag && symbol.sourceFile != null =>
symbol.owner.info.decls.lookupAll(name).toList match {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I don't immediately understand; lookup for name failed (we're about to issue an error), but lookupSymbol(name.toTermName).owner.info.decls.lookupAll(name) gives a result. How is that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package is loaded from class path first, which creates symbols of existing class + its module & module class. Then compilation of source takes over the existing module symbol, which is entered into scope.

It's fishy that the package class has a set of members but not all are in scope. Previously I've read a comment about stale symbols from previous run; symbol history, like human history, is a story of wreckage and detritus. Somehow I had never fully considered the state of the environment during incremental compilation.

NormalTypeError(tree, s"not found: ${decodeWithKind(name, owner)}$help")
def SymbolNotFoundError(tree: Tree, name: Name, owner: Symbol, inPattern: Boolean, hidden: Boolean) = {
val help = if (inPattern && name.isTermName) s"\nIdentifiers ${if (name.charAt(0).isUpper) "that begin with uppercase" else "enclosed in backticks"} are not pattern variables but match the value in scope." else ""
val path = if (hidden) s"\nA definition on the class path may be hidden by a source artifact. (Companions must be defined together.)" else ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can find a message that's a bit more explicit, hands-on. Connecting the dots between a "definition hidden by a source artifact" and "companions" is not easy simple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants