diff --git a/scalafix-core/src/main/scala/scalafix/internal/util/EagerInMemorySemanticdbIndex.scala b/scalafix-core/src/main/scala/scalafix/internal/util/EagerInMemorySemanticdbIndex.scala index d0f662f5a..ef2374e87 100644 --- a/scalafix-core/src/main/scala/scalafix/internal/util/EagerInMemorySemanticdbIndex.scala +++ b/scalafix-core/src/main/scala/scalafix/internal/util/EagerInMemorySemanticdbIndex.scala @@ -5,6 +5,7 @@ import scala.meta._ import scala.meta.internal.symtab._ import scala.meta.internal.{semanticdb => s} import scalafix.internal.v0._ +import scalafix.internal.v1.TreePos import scalafix.util.SemanticdbIndex import scalafix.v0 import scalafix.v0._ @@ -50,16 +51,7 @@ case class EagerInMemorySemanticdbIndex( def symbol(position: Position): Option[Symbol] = _names.get(position).map(_.symbol) def symbol(tree: Tree): Option[Symbol] = tree match { - case name @ Name(_) => - val syntax = name.syntax - // workaround for https://github.com/scalameta/scalameta/issues/1083 - val pos = - if (syntax.startsWith("(") && - syntax.endsWith(")") && - syntax != name.value) - Position.Range(name.pos.input, name.pos.start + 1, name.pos.end - 1) - else name.pos - symbol(pos) + case name: Name => TreePos.symbol[Option[Symbol]](name, symbol, _.isEmpty) case Importee.Rename(name, _) => symbol(name) case Importee.Name(name) => symbol(name) case Term.Select(_, name @ Name(_)) => symbol(name) diff --git a/scalafix-core/src/main/scala/scalafix/internal/v0/LegacySemanticdbIndex.scala b/scalafix-core/src/main/scala/scalafix/internal/v0/LegacySemanticdbIndex.scala index d41b7cb81..88558fb51 100644 --- a/scalafix-core/src/main/scala/scalafix/internal/v0/LegacySemanticdbIndex.scala +++ b/scalafix-core/src/main/scala/scalafix/internal/v0/LegacySemanticdbIndex.scala @@ -71,7 +71,7 @@ final class LegacySemanticdbIndex(val doc: v1.SemanticDocument) case multi => Some(v0.Symbol.Multi(multi)) } override def symbol(tree: Tree): Option[v0.Symbol] = - symbol(TreePos.symbol(tree)) + TreePos.symbolImpl[Option[v0.Symbol]](tree)(symbol, _.isEmpty) override def denotation(symbol: v0.Symbol): Option[v0.Denotation] = { doc.internal.info(v1.Symbol(symbol.syntax)).map { info => diff --git a/scalafix-core/src/main/scala/scalafix/internal/v1/InternalSemanticDoc.scala b/scalafix-core/src/main/scala/scalafix/internal/v1/InternalSemanticDoc.scala index 240c14361..3ae3462a5 100644 --- a/scalafix-core/src/main/scala/scalafix/internal/v1/InternalSemanticDoc.scala +++ b/scalafix-core/src/main/scala/scalafix/internal/v1/InternalSemanticDoc.scala @@ -50,7 +50,8 @@ final class InternalSemanticDoc( def symbol(tree: Tree): Symbol = { def fromTextDocument() = { - val result = symbols(TreePos.symbol(tree)) + val result = + TreePos.symbolImpl[Iterator[Symbol]](tree)(symbols, _.isEmpty) if (result.hasNext) result.next() // Discard multi symbols else Symbol.None } diff --git a/scalafix-core/src/main/scala/scalafix/internal/v1/TreePos.scala b/scalafix-core/src/main/scala/scalafix/internal/v1/TreePos.scala index 78b8f7af1..1aec4a16b 100644 --- a/scalafix-core/src/main/scala/scalafix/internal/v1/TreePos.scala +++ b/scalafix-core/src/main/scala/scalafix/internal/v1/TreePos.scala @@ -4,49 +4,67 @@ import scala.annotation.tailrec import scala.meta._ object TreePos { - @tailrec def symbol(tree: Tree): Position = tree match { - case name @ Name(_) => - val syntax = name.syntax - // workaround for https://github.com/scalameta/scalameta/issues/1083 - if (syntax.startsWith("(") && - syntax.endsWith(")") && - syntax != name.value) { + private[scalafix] def symbol[T]( + name: Name, + f: Position => T, + isEmpty: T => Boolean + ): T = { + val syntax = name.syntax + // workaround for https://github.com/scalameta/scalameta/issues/1083 + if (syntax.startsWith("(") && + syntax.endsWith(")") && + syntax != name.value) { + val pos = Position.Range(name.pos.input, name.pos.start + 1, name.pos.end - 1) - } else { - name.pos - } - case m: Member => symbol(m.name) - case t: Term.Select => symbol(t.name) - case t: Term.Interpolate => symbol(t.prefix) - case t: Term.Apply => symbol(t.fun) - case t: Term.ApplyInfix => symbol(t.op) - case t: Term.ApplyUnary => symbol(t.op) - case t: Term.ApplyType => symbol(t.fun) - case t: Term.Assign => symbol(t.lhs) - case t: Term.Ascribe => symbol(t.expr) - case t: Term.Annotate => symbol(t.expr) - case t: Term.New => symbol(t.init) - case t: Type.Select => symbol(t.name) - case t: Type.Project => symbol(t.name) - case t: Type.Singleton => symbol(t.ref) - case t: Type.Apply => symbol(t.tpe) - case t: Type.ApplyInfix => symbol(t.op) - case t: Type.Annotate => symbol(t.tpe) - case t: Type.ByName => symbol(t.tpe) - case t: Type.Repeated => symbol(t.tpe) - case t: Pat.Bind => symbol(t.lhs) - case t: Pat.Extract => symbol(t.fun) - case t: Pat.ExtractInfix => symbol(t.op) - case t: Pat.Interpolate => symbol(t.prefix) - case Defn.Val(_, p :: Nil, _, _) => symbol(p) - case Decl.Val(_, p :: Nil, _) => symbol(p) - case Defn.Var(_, p :: Nil, _, _) => symbol(p) - case Decl.Var(_, p :: Nil, _) => symbol(p) - case t: Importee.Rename => symbol(t.name) - case t: Importee.Name => symbol(t.name) - case Importer(_, i :: Nil) => symbol(i) - case t: Init => symbol(t.tpe) - case t: Mod.Annot => symbol(t.init) + val t = f(pos) + if (isEmpty(t)) f(name.pos) + else t + } else { + f(name.pos) + } + } + + def symbol(tree: Tree): Position = + symbolImpl[Position](tree)(identity[Position], _ => false) + + // The implicit function must named `$conforms` to shadow `Predef.$conforms` + @tailrec + private[scalafix] def symbolImpl[T]( + tree: Tree + )(implicit $conforms: Position => T, isEmpty: T => Boolean): T = tree match { + case name: Name => symbol(name, $conforms, isEmpty) + case m: Member => symbolImpl(m.name) + case t: Term.Select => symbolImpl(t.name) + case t: Term.Interpolate => symbolImpl(t.prefix) + case t: Term.Apply => symbolImpl(t.fun) + case t: Term.ApplyInfix => symbolImpl(t.op) + case t: Term.ApplyUnary => symbolImpl(t.op) + case t: Term.ApplyType => symbolImpl(t.fun) + case t: Term.Assign => symbolImpl(t.lhs) + case t: Term.Ascribe => symbolImpl(t.expr) + case t: Term.Annotate => symbolImpl(t.expr) + case t: Term.New => symbolImpl(t.init) + case t: Type.Select => symbolImpl(t.name) + case t: Type.Project => symbolImpl(t.name) + case t: Type.Singleton => symbolImpl(t.ref) + case t: Type.Apply => symbolImpl(t.tpe) + case t: Type.ApplyInfix => symbolImpl(t.op) + case t: Type.Annotate => symbolImpl(t.tpe) + case t: Type.ByName => symbolImpl(t.tpe) + case t: Type.Repeated => symbolImpl(t.tpe) + case t: Pat.Bind => symbolImpl(t.lhs) + case t: Pat.Extract => symbolImpl(t.fun) + case t: Pat.ExtractInfix => symbolImpl(t.op) + case t: Pat.Interpolate => symbolImpl(t.prefix) + case Defn.Val(_, p :: Nil, _, _) => symbolImpl(p) + case Decl.Val(_, p :: Nil, _) => symbolImpl(p) + case Defn.Var(_, p :: Nil, _, _) => symbolImpl(p) + case Decl.Var(_, p :: Nil, _) => symbolImpl(p) + case t: Importee.Rename => symbolImpl(t.name) + case t: Importee.Name => symbolImpl(t.name) + case Importer(_, i :: Nil) => symbolImpl(i) + case t: Init => symbolImpl(t.tpe) + case t: Mod.Annot => symbolImpl(t.init) case _ => tree.pos } } diff --git a/scalafix-docs/src/main/scala/scalafix/docs/PatchDocs.scala b/scalafix-docs/src/main/scala/scalafix/docs/PatchDocs.scala index 306b87288..a6bdd09c7 100644 --- a/scalafix-docs/src/main/scala/scalafix/docs/PatchDocs.scala +++ b/scalafix-docs/src/main/scala/scalafix/docs/PatchDocs.scala @@ -1,7 +1,5 @@ package scalafix.docs -import java.nio.file.Paths -import java.io.File import org.typelevel.paiges.Doc import scala.meta.inputs.Input import scala.meta.interactive.InteractiveSemanticdb diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala b/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala index 3c1af0556..da6f175b6 100644 --- a/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala +++ b/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala @@ -41,7 +41,7 @@ case class LegacyInMemorySemanticdbIndex( } } override def symbol(tree: Tree): Option[v0.Symbol] = - symbol(TreePos.symbol(tree)) + TreePos.symbolImpl[Option[v0.Symbol]](tree)(symbol, _.isEmpty) override def denotation(symbol: v0.Symbol): Option[Denotation] = symbol match { case v0.Symbol.Local(id) => diff --git a/scalafix-rules/src/main/scala/scalafix/internal/rule/ExplicitResultTypes.scala b/scalafix-rules/src/main/scala/scalafix/internal/rule/ExplicitResultTypes.scala index a00a669ab..ec03025a7 100644 --- a/scalafix-rules/src/main/scala/scalafix/internal/rule/ExplicitResultTypes.scala +++ b/scalafix-rules/src/main/scala/scalafix/internal/rule/ExplicitResultTypes.scala @@ -10,7 +10,6 @@ import scala.meta.internal.pc.ScalafixGlobal import scalafix.internal.v1.LazyValue import scala.util.control.NonFatal import scala.util.Properties -import metaconfig.Conf import scalafix.internal.compat.CompilerCompat._ final class ExplicitResultTypes( diff --git a/scalafix-tests/shared/src/main/scala/test/SymbolTest.scala b/scalafix-tests/shared/src/main/scala/test/SymbolTest.scala index 791df13bf..e13fbc4a5 100644 --- a/scalafix-tests/shared/src/main/scala/test/SymbolTest.scala +++ b/scalafix-tests/shared/src/main/scala/test/SymbolTest.scala @@ -7,3 +7,8 @@ object a { object d } } +trait SymbolTest { + def shouldBe(right: Any) + def arg = 1 + this shouldBe (arg) +} diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/v1/SymbolSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/v1/SymbolSuite.scala index abebf682c..7bfc90805 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/v1/SymbolSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/v1/SymbolSuite.scala @@ -1,11 +1,10 @@ package scalafix.tests.v1 -import org.scalatest.FunSuite -import scala.meta.Import -import scala.meta.Importer +import scala.meta._ import scalafix.tests.core.BaseSemanticSuite +import scalafix.v1._ -class SymbolSuite extends FunSuite { +class SymbolSuite extends munit.FunSuite { implicit val doc = BaseSemanticSuite.loadDoc("SymbolTest.scala") test("normalized") { @@ -13,7 +12,14 @@ class SymbolSuite extends FunSuite { case Import(Importer(ref, _) :: Nil) => ref } - import scalafix.v1._ - assert(ref.symbol.normalized.owner.value == "test.a.") + assertEquals(ref.symbol.normalized.owner.value, "test.a.") + } + + test("fromTextDocument") { + val arg :: Nil = doc.tree.collect { + case Term.ApplyInfix(_, Term.Name("shouldBe"), _, arg :: Nil) => arg + } + + assertNotEquals(arg.symbol, Symbol.None) } }