Skip to content

False positive warning: unused import with certain combination of generics #22971

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

Open
OndrejSpanel opened this issue Apr 11, 2025 · 6 comments · May be fixed by #22982
Open

False positive warning: unused import with certain combination of generics #22971

OndrejSpanel opened this issue Apr 11, 2025 · 6 comments · May be fixed by #22982
Assignees
Labels
area:linting Linting warnings enabled with -W or -Xlint itype:bug

Comments

@OndrejSpanel
Copy link
Member

OndrejSpanel commented Apr 11, 2025

Compiler version

3.7.0-RC1
3.7.0-RC2

Minimized code

Compile with -Wunused:imports:

trait Base
class Class extends Base

abstract class Entity[T: GetType]

class Thing extends Entity[Class]

trait GetType[T]

object GetType {
  implicit object GetTypeClass extends GetType[Class]
}
object Main {
  def main(args: Array[String]): Unit = {
    import GetType.*
    val e = GetTypeClass
  }
}

Output

[warn] 15 |    import GetType.*
[warn]    |                   ^
[warn]    |                   unused import

Expectation

The code does not compile without the import, the warning should not be printed.

Note

The warning goes away when you remove the line class Thing extends Entity[Class].

@OndrejSpanel OndrejSpanel added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Apr 11, 2025
@som-snytt som-snytt self-assigned this Apr 11, 2025
@som-snytt som-snytt added area:linting Linting warnings enabled with -W or -Xlint and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Apr 11, 2025
@som-snytt
Copy link
Contributor

som-snytt commented Apr 11, 2025

This is due to caching the resolution of GetType.GetTypeClass, which appears as identical Ident trees in the parent of Thing and in the RHS of e.

From the superclass context of Thing, it incorrectly cached at an outer context, where the lookup from e found it as a definition at higher precedence than the import.

A couple of notes to self or reminders:

The trees don't distinguish whether the ident is due to implicit search or import. The comment at typedIdent is false, because the elidable prefix does not result in a Select. That is, I'd expect the RHS of e to be a typed selection.

Scala 2 certainly transforms to a select:

Select(Ident(p.GetType), TermName("GetTypeClass"))

or -Yprint-trees:format

          ValDef( // val e: p.GetType[p.Class]
            0
            "e"
            <tpt> // tree.tpe=p.GetType[p.Class]
            "GetType"."GetTypeClass" // implicit val GetTypeClass: p.GetType[p.Class] in object GetType, tree.tpe=p.GetType[p.Class]
          )

This doesn't matter for Scala 2 -Wunused:imports because the typer records the imports it used for lookups.

Removing the loopOverPrefixes doesn't break any tests. Either that progressed elsewhere or a better test is needed. (I think I restored that code at some point last year, but I don't have commit history to see why.) (Edit: https://github.com/scala/scala3/pull/22751/files)

@OndrejSpanel
Copy link
Member Author

Is there any plan to fix this and #22971 for 3.7.0 release? Both issues have PRs waiting for review.

If this is not fixed, how can I make my project compile other than not using -Xfatal-warnings or -Wunused:imports?

I did not find a way how to disable import warnings selectively, as I was not able to make Wconf match properly on the multiline message.

@som-snytt
Copy link
Contributor

som-snytt commented Apr 25, 2025

@OndrejSpanel I think lint fixes will not be "backported" to the branch during RC at this point.

Because it is "just a lint", the immediate issue is that -Wconf isn't working for you. For imports, Scala 3 now has origin=my.pkg. That matches the import "selector", and its purpose is to work where @nowarn fails.

Your other question about matching multiline messages will require some testing, but supposedly the Wconf expression is "just a regex". (Edit: the regex need match only a subsequence of the text, i.e., findFirstIn.) I'll either follow up here or open a new ticket if I can't get it to work.

If you post your -Wconf here, I'll look at it. I would agree that wconf is a "cottage industry" unto itself.

@som-snytt
Copy link
Contributor

It seems the best expedient is -Wconf:origin=p.q.X.* where a wildcard must be asterisk or it could be a specific name. I didn't get it to work for rename. It matches just one selector, so if there are braces in source p.{A, B} just use p.A.

I tried //> using options "-Wconf:msg=p1.A.*\\R.*\\^.*\\R.*unused:s" with scala-cli.

It doesn't like single backslash. Not sure what emits the message.

[error] ./tests/warn/i15503a.scala:2:38
[error] invalid escape character
[error] //> using options "-Wconf:msg=p1.A.*\R.*\^.*\R.*unused:s"
[error]                                      ^

Here is a test of the regex.

val text = """
    |      import p1.A   // warn
    |                ^
    |                unused import
    |""".stripMargin

val r = "p1.A.*\\R.*\\^.*\\R.*unused".r

@main def test = println:
  r.findFirstIn(text)

which is

Some(p1.A   // warn
                ^
                unused)

(I don't bother to escape all the dots.)

It's probably worth exercising these different approaches to know what works or should work. I didn't try a "regular" build file.

@OndrejSpanel
Copy link
Member Author

OndrejSpanel commented Apr 30, 2025

I am trying how to suppress:

This works:

  "-Wconf:origin=some\\.example:s"
  "-Wconf:id=E198&origin=some\\.example:s"
  "-Wconf:name=UnusedSymbol&origin=some\\.example:s"
  "-Wconf:msg=unused import&origin=some\\.example:s",

This does not:

  "-Wconf:cat=unused&origin=some\\.example:s"

When trying to match msg, this suppresses (but all unused imports):

  "-Wconf:msg=unused import:s"

This did not work:

  "-Wconf:msg=import some\\.example.*\\R.*\\^.*\\R.*unused import:s",
  "-Wconf:msg=.*\\R.*unused import:s",

It seems the message is actually only "unused import", not multiline as I assumed. As it does not contain the symbol name, it is impossible to match against it - at least this is how I understand it.

I have tried finding some documentation. what I have found is Scala 2.13 one, but nothing for Scala 3, and the options seem to have changed, e.g. -Wconf:any:wv does not seem to work in Scala 3.

It seems the most accurate documentation is available by running scalac -Wconf:help, but perhaps it could be placed somewhere on the web page as well?

@som-snytt
Copy link
Contributor

Yes, the help text is vitally important and belongs somewhere accessible.

Thanks for the trials. I got as far as noticing that the msg text doesn't include the error id, but what you say makes sense, the printed output just has the msg itself embedded. That explains it!

I always start by blaming regex, especially after midnight.

Oh, I see scala-cli allows

scala-cli compile --server=false -S 3.7.0-RC4 -Wconf:help huh.scala

which is more convenient than I expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:linting Linting warnings enabled with -W or -Xlint itype:bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants