diff --git a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala index ef583ea2a225..9f42a8f0a408 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala @@ -100,9 +100,9 @@ object MetalsInteractive: pos: SourcePosition, indexed: IndexedContext, skipCheckOnName: Boolean = false - ): List[Symbol] = + )(using Context): List[Symbol] = enclosingSymbolsWithExpressionType(path, pos, indexed, skipCheckOnName) - .map(_._1) + .map(_._1.sourceSymbol) /** * Returns the list of tuple enclosing symbol and @@ -217,6 +217,15 @@ object MetalsInteractive: val tpe = getIndex(t2).getOrElse(NoType) List((ddef.symbol, tpe, Some(name))) + case head :: (sel @ Select(_, name)) :: _ + if head.sourcePos.encloses(sel.sourcePos) && (name == StdNames.nme.apply || name == StdNames.nme.unapply) => + val optObjectSymbol = List(head.symbol).filter(sym => !(sym.is(Synthetic) && sym.is(Module))) + val classSymbol = head.symbol.companionClass + val optApplySymbol = List(sel.symbol).filter(sym => !sym.is(Synthetic)) + val symbols = optObjectSymbol ++ (classSymbol :: optApplySymbol) + symbols.collect: + case sym if sym.exists => (sym, sym.info, None) + case path @ head :: tail => if head.symbol.is(Exported) then val sym = head.symbol.sourceSymbol diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala index 8ff43ba07358..9fbaa8dcd16d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala @@ -79,7 +79,7 @@ class PcDefinitionProvider( .untypedPath(pos.span) .collect { case t: untpd.Tree => t } - definitionsForSymbol(untpdPath.headOption.map(_.symbol).toList, uri, pos) + definitionsForSymbols(untpdPath.headOption.map(_.symbol).toList, uri, pos) end fallbackToUntyped private def findDefinitions( @@ -89,7 +89,7 @@ class PcDefinitionProvider( uri: URI, ): DefinitionResult = import indexed.ctx - definitionsForSymbol( + definitionsForSymbols( MetalsInteractive.enclosingSymbols(path, pos, indexed), uri, pos @@ -113,68 +113,58 @@ class PcDefinitionProvider( case Nil => path.headOption match case Some(value: Literal) => - definitionsForSymbol(List(value.typeOpt.widen.typeSymbol), uri, pos) + definitionsForSymbols(List(value.typeOpt.widen.typeSymbol), uri, pos) case _ => DefinitionResultImpl.empty case _ => - definitionsForSymbol(typeSymbols, uri, pos) - + definitionsForSymbols(typeSymbols, uri, pos) end findTypeDefinitions - private def definitionsForSymbol( + private def definitionsForSymbols( symbols: List[Symbol], uri: URI, pos: SourcePosition )(using ctx: Context): DefinitionResult = - symbols match - case symbols @ (sym :: other) => - val isLocal = sym.source == pos.source - if isLocal then - val include = Include.definitions | Include.local - val (exportedDefs, otherDefs) = - Interactive.findTreesMatching(driver.openedTrees(uri), include, sym) - .partition(_.tree.symbol.is(Exported)) - - otherDefs.headOption.orElse(exportedDefs.headOption) match - case Some(srcTree) => - val pos = srcTree.namePos - if pos.exists then - val loc = new Location(params.uri().toString(), pos.toLsp) - DefinitionResultImpl( - SemanticdbSymbols.symbolName(sym), - List(loc).asJava, - ) - else DefinitionResultImpl.empty - case None => - DefinitionResultImpl.empty - else - val res = new ArrayList[Location]() - semanticSymbolsSorted(symbols) - .foreach { sym => - res.addAll(search.definition(sym, params.uri())) - } - DefinitionResultImpl( - SemanticdbSymbols.symbolName(sym), - res - ) - end if + semanticSymbolsSorted(symbols) match case Nil => DefinitionResultImpl.empty - end match - end definitionsForSymbol + case syms @ ((_, headSym) :: tail) => + val locations = syms.flatMap: + case (sym, semanticdbSymbol) => + locationsForSymbol(sym, semanticdbSymbol, uri, pos) + DefinitionResultImpl(headSym, locations.asJava) + + private def locationsForSymbol( + symbol: Symbol, + semanticdbSymbol: String, + uri: URI, + pos: SourcePosition + )(using ctx: Context): List[Location] = + val isLocal = symbol.source == pos.source + if isLocal then + val trees = driver.openedTrees(uri) + val include = Include.definitions | Include.local + val (exportedDefs, otherDefs) = + Interactive.findTreesMatching(trees, include, symbol) + .partition(_.tree.symbol.is(Exported)) + otherDefs.headOption.orElse(exportedDefs.headOption).collect: + case srcTree if srcTree.namePos.exists => + new Location(params.uri().toString(), srcTree.namePos.toLsp) + .toList + else search.definition(semanticdbSymbol, uri).asScala.toList def semanticSymbolsSorted( syms: List[Symbol] - )(using ctx: Context): List[String] = + )(using ctx: Context): List[(Symbol, String)] = syms - .map { sym => + .collect { case sym if sym.exists => // in case of having the same type and teerm symbol // term comes first // used only for ordering symbols that come from `Import` val termFlag = if sym.is(ModuleClass) then sym.sourceModule.isTerm else sym.isTerm - (termFlag, SemanticdbSymbols.symbolName(sym)) + (termFlag, sym.sourceSymbol, SemanticdbSymbols.symbolName(sym)) } - .sorted - .map(_._2) + .sortBy { case (termFlag, _, name) => (termFlag, name) } + .map(_.tail) end PcDefinitionProvider diff --git a/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala index 3fb97aaeca4e..a87c8b4d6105 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala @@ -512,3 +512,75 @@ class PcDefinitionSuite extends BasePcDefinitionSuite: |val foo_name = foo.na@@me |""".stripMargin ) + + @Test def `object` = + check( + """|package a + |object <> { + | def foo = 42 + |} + |val m = B@@ar.foo + |""".stripMargin + ) + + @Test def i7267 = + check( + """|package a + |trait Foo { + | def someNum: Int + | def <>(i: Int): Unit = println(someNum) + |} + |object <> extends Foo { + | def someNum = 42 + |} + | + |object Test { + | B@@ar(2) + |} + |""".stripMargin + ) + + @Test def `i7267-2` = + check( + """|package b + |trait Foo { + | def someNum: Int + | def <>(i: Int): Option[Int] = Some(i) + |} + |object <> extends Foo { + | def someNum = 42 + |} + | + |object Test { + | Bar.someNum match { + | case B@@ar(1) => ??? + | case _ => + | } + |} + |""".stripMargin + ) + + @Test def `i7267-3` = + check( + """|package c + |case class <>() + |object <> + |object O { + | val a = B@@ar() + |} + |""".stripMargin + ) + + @Test def `i7267-4` = + check( + """|package d + |class <>() + |object <> { + | def <>(): Bar = new Bar() + |} + |object O { + | val a = B@@ar() + |} + |""".stripMargin + ) +