scalafix icon indicating copy to clipboard operation
scalafix copied to clipboard

Broken signatures: scala.meta.internal.classpath.MissingSymbolException: missing symbol

Open liancheng opened this issue 5 years ago • 2 comments
trafficstars

TL;DR

Hit scalafix.internal.v1.FileException while testing a snapshot release (0.3.0+9-86c91c86-SNAPSHOT) of the OrganizeImports rule against Metals master.

How to reproduce

  1. Checkout scalameta/metals@7b88c09

  2. Apply the following diff:

    diff --git a/.scalafix.conf b/.scalafix.conf
    index 6d486e27..7edcce92 100644
    --- a/.scalafix.conf
    +++ b/.scalafix.conf
    @@ -1,6 +1,11 @@
     rules = [
    -  RemoveUnused,
    +  OrganizeImports,
       ExplicitResultTypes,
     ]
    
     ExplicitResultTypes.rewriteStructuralTypesToNamedSubclass = false
    +
    +OrganizeImports {
    +  expandRelative = true
    +  groups = ["re:javax?\\.", "scala.", "scala.meta.", "*"]
    +}
    diff --git a/build.sbt b/build.sbt
    index e405fe98..0fea2ea1 100644
    --- a/build.sbt
    +++ b/build.sbt
    @@ -31,6 +31,7 @@ val MUnitFramework = new TestFramework("munit.Framework")
    
     inThisBuild(
       List(
    +    scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.3.0+9-86c91c86-SNAPSHOT",
         version ~= { dynVer =>
           if (isCI) dynVer
           else localSnapshotVersion // only for local publishing
    
  3. Running sbt scalafix produces the following exception on two source files:

    scalafix.internal.v1.FileException: unexpected error processing file <file-path>
    Caused by: scala.meta.internal.classpath.MissingSymbolException: missing symbol: snailgun
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSpec.descriptor(SymbolOps.scala:119)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.uncached$1(SymbolOps.scala:18)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.toSemantic(SymbolOps.scala:25)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbol.ssym(SymbolOps.scala:185)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSpec.owner(SymbolOps.scala:58)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.uncached$1(SymbolOps.scala:18)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.toSemantic(SymbolOps.scala:25)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbol.ssym(SymbolOps.scala:185)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSpec.owner(SymbolOps.scala:58)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.uncached$1(SymbolOps.scala:18)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.toSemantic(SymbolOps.scala:25)
            at scala.meta.internal.scalacp.SymbolOps$XtensionSymbol.ssym(SymbolOps.scala:185)
            at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.loop$1(TypeOps.scala:22)
            at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.toSemanticTpe(TypeOps.scala:94)
            at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.loop$2(TypeOps.scala:136)
            at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.toSemanticSig(TypeOps.scala:139)
            at scala.meta.internal.scalacp.SymbolInformationOps$XtensionGSymbolSSymbolInformation.sig(SymbolInformationOps.scala:164)
            at scala.meta.internal.scalacp.SymbolInformationOps$XtensionGSymbolSSymbolInformation.toSymbolInformation(SymbolInformationOps.scala:251)
            at scala.meta.internal.scalacp.Scalacp.sinfos(Scalacp.scala:32)
            at scala.meta.internal.scalacp.Scalacp.$anonfun$parse$1(Scalacp.scala:20)
            at scala.collection.immutable.List.flatMap(List.scala:338)
            at scala.meta.internal.scalacp.Scalacp.parse(Scalacp.scala:19)
            at scala.meta.internal.scalacp.Scalacp$.parse(Scalacp.scala:50)
            at scala.meta.internal.metacp.ClassfileInfos$.fromClassNode(ClassfileInfos.scala:47)
            at scala.meta.internal.symtab.GlobalSymbolTable.loadSymbol(GlobalSymbolTable.scala:38)
            at scala.meta.internal.symtab.GlobalSymbolTable.info(GlobalSymbolTable.scala:67)
            at scalafix.internal.v1.InternalSemanticDoc.info(InternalSemanticDoc.scala:71)
            at scalafix.v1.SemanticDocument.info(SemanticDocument.scala:38)
            at scalafix.v1.Symbol.info(Symbol.scala:21)
            at fix.OrganizeImports.$anonfun$partitionImplicits$3(OrganizeImports.scala:196)
            at fix.OrganizeImports.$anonfun$partitionImplicits$3$adapted(OrganizeImports.scala:196)
            at scala.collection.TraversableLike.noneIn$1(TraversableLike.scala:306)
            at scala.collection.TraversableLike.filterImpl(TraversableLike.scala:372)
            at scala.collection.TraversableLike.filterImpl$(TraversableLike.scala:284)
            at scala.collection.AbstractTraversable.filterImpl(Traversable.scala:108)
            at scala.collection.TraversableLike.filter(TraversableLike.scala:382)
            at scala.collection.TraversableLike.filter$(TraversableLike.scala:382)
            at scala.collection.AbstractTraversable.filter(Traversable.scala:108)
            at fix.OrganizeImports.$anonfun$partitionImplicits$1(OrganizeImports.scala:196)
            at scala.collection.TraversableLike.$anonfun$flatMap$1(TraversableLike.scala:280)
            at scala.collection.immutable.List.foreach(List.scala:392)
            at scala.collection.TraversableLike.flatMap(TraversableLike.scala:280)
            at scala.collection.TraversableLike.flatMap$(TraversableLike.scala:277)
            at scala.collection.immutable.List.flatMap(List.scala:355)
            at fix.OrganizeImports.partitionImplicits(OrganizeImports.scala:192)
            at fix.OrganizeImports.organizeGlobalImports(OrganizeImports.scala:90)
            at fix.OrganizeImports.fix(OrganizeImports.scala:77)
            at scalafix.internal.v1.Rules.$anonfun$semanticPatch$1(Rules.scala:75)
            at scala.collection.immutable.List.map(List.scala:286)
            at scalafix.internal.v1.Rules.semanticPatch(Rules.scala:73)
            at scalafix.internal.v1.MainOps$.unsafeHandleFile(MainOps.scala:234)
    

    The problematic source files are:

    metals/src/main/scala/scala/meta/internal/metals/BloopServers.scala
    metals/src/main/scala/scala/meta/internal/pantsbuild/commands/SharedCommand.scala
    

    This exception is triggered by a Symbol#info call in this line.

liancheng avatar May 14 '20 07:05 liancheng

I have had a similar issue on sources using the wire macro from https://github.com/softwaremill/macwire, resulting in MissingSymbolException: com.softwaremill.macwire.Wired (and that macro only, which should NOT exercise https://github.com/softwaremill/macwire/blob/d509b04bb95567a935497bbef186875c60fce13b/macros/src/main/scala/com/softwaremill/macwire/MacwireMacros.scala#L118).

Changing in these source files the global import from com.softwaremill.macwire.wire (which is enough to compile) to com.softwaremill.macwire._ did the trick. https://github.com/liancheng/scalafix-organize-imports/pull/51 also silences the issue.

I'll try to write a repro.

bjaglin avatar May 14 '20 14:05 bjaglin

I got a repro: https://github.com/bjaglin/scalafix/commit/d1d6afdd964cfe1835e248f649c0e4ad507234d2

My specific problem (which might not be the one originally reported, but looks very similar), is related to a broken runtime dependency: "com.softwaremill.macwire" %% "macros" is built against "com.softwaremill.macwire" %% "macros % Provided", so when the latter is missing in the classpath (which is weird but a common/documented usage of the lib), lookups on a symbol that is in a class file that refer to one that was provided at compilation time fail.

So when looking up com.softwaremill.macwire.package.wire(), we get through https://github.com/softwaremill/macwire/blob/release-2.3.3/macros/src/main/scala/com/softwaremill/macwire/package.scala#L33 (which happens to be a macro, but I don't think it matters):

missing symbol: com.softwaremill.macwire.Wired
scala.meta.internal.classpath.MissingSymbolException: missing symbol: com.softwaremill.macwire.Wired
	at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSpec.descriptor(SymbolOps.scala:119)
	at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.uncached$1(SymbolOps.scala:18)
	at scala.meta.internal.scalacp.SymbolOps$XtensionSymbolSSymbol.toSemantic(SymbolOps.scala:25)
	at scala.meta.internal.scalacp.SymbolOps$XtensionSymbol.ssym(SymbolOps.scala:185)
	at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.loop$1(TypeOps.scala:22)
	at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.toSemanticTpe(TypeOps.scala:94)
	at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.loop$2(TypeOps.scala:108)
	at scala.meta.internal.scalacp.TypeOps$XtensionTypeSType.toSemanticSig(TypeOps.scala:139)
	at scala.meta.internal.scalacp.SymbolInformationOps$XtensionGSymbolSSymbolInformation.sig(SymbolInformationOps.scala:164)
	at scala.meta.internal.scalacp.SymbolInformationOps$XtensionGSymbolSSymbolInformation.toSymbolInformation(SymbolInformationOps.scala:251)
	at scala.meta.internal.scalacp.Scalacp.sinfos(Scalacp.scala:32)
	at scala.meta.internal.scalacp.Scalacp.$anonfun$parse$1(Scalacp.scala:20)
	at scala.collection.immutable.List.flatMap(List.scala:338)
	at scala.meta.internal.scalacp.Scalacp.parse(Scalacp.scala:19)
	at scala.meta.internal.scalacp.Scalacp$.parse(Scalacp.scala:50)
	at scala.meta.internal.metacp.ClassfileInfos$.fromClassNode(ClassfileInfos.scala:47)
	at scala.meta.internal.symtab.GlobalSymbolTable.loadSymbol(GlobalSymbolTable.scala:38)
	at scala.meta.internal.symtab.GlobalSymbolTable.info(GlobalSymbolTable.scala:67)
	at scalafix.internal.v1.InternalSemanticDoc.info(InternalSemanticDoc.scala:72)
	at scalafix.v1.SemanticDocument.info(SemanticDocument.scala:38)
	at scalafix.v1.Symbol.info(Symbol.scala:21)

It looks like scalameta has support for a graceful handling of this via https://github.com/scalameta/scalameta/blob/v4.3.10/semanticdb/metacp/src/main/scala/scala/meta/internal/scalacp/SymbolInformationOps.scala#L188-L192. However, in the current codepath, there is no way to enable that stubBrokenSignatures flag since settings are not exposed. I guess that would be doable?

Letting the exception bubble up to scalafix which would recover to None is not an option I guess, since in that case, we do want to return the information we have about the symbol we are looking up - it's just that one of the overall parsing of the class fail in https://github.com/scalameta/scalameta/blob/v4.3.10/semanticdb/metacp/src/main/scala/scala/meta/internal/scalacp/Scalacp.scala#L19-L20.

bjaglin avatar May 15 '20 02:05 bjaglin