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

Find References searches also for base method occurrences #1388

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package scala.meta.internal.implementation

import org.eclipse.lsp4j.Location
import org.eclipse.lsp4j.TextDocumentPositionParams
import scala.meta.internal.mtags.Semanticdbs
import scala.meta.internal.mtags.{Symbol => MSymbol}
import scala.meta.internal.metals.MetalsEnrichments._
Expand All @@ -12,14 +10,13 @@ import scala.meta.internal.semanticdb.ClassSignature
import scala.meta.internal.semanticdb.TypeRef
import scala.meta.internal.semanticdb.Signature
import scala.meta.internal.semanticdb.TextDocument
import java.util.concurrent.ConcurrentHashMap
import java.nio.file.Path
import scala.meta.internal.semanticdb.SymbolInformation
import scala.meta.internal.semanticdb.MethodSignature
import scala.meta.internal.mtags.GlobalSymbolIndex
import scala.meta.internal.metals.BuildTargets
import scala.meta.internal.metals.Buffers
import scala.meta.internal.metals.BuildTargets
import scala.meta.internal.metals.DefinitionProvider
import scala.meta.internal.metals.FilePosition
import scala.meta.internal.metals.TokenEditDistance
import scala.meta.internal.semanticdb.Scala._
import scala.meta.internal.semanticdb.TypeSignature
Expand All @@ -28,6 +25,10 @@ import scala.meta.internal.symtab.GlobalSymbolTable
import scala.util.control.NonFatal
import scala.meta.internal.mtags.Mtags
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.ConcurrentHashMap
import java.nio.file.Path
import org.eclipse.lsp4j.Location
import org.eclipse.lsp4j.TextDocumentPositionParams

final class ImplementationProvider(
semanticdbs: Semanticdbs,
Expand Down Expand Up @@ -82,93 +83,122 @@ final class ImplementationProvider(
}
}

def implementations(position: TextDocumentPositionParams): List[Location] = {
implementations(
Left(
FilePosition(
position.getTextDocument.getUri.toAbsolutePath,
position.getPosition
)
),
position.getTextDocument.getUri.toAbsolutePath
)
}

def implementations(
params: TextDocumentPositionParams
filePositionOrSymbol: Either[FilePosition, SymbolInformation],
source: AbsolutePath
): List[Location] = {
filePositionOrSymbol match {
case Left(filePosition) =>
definitionProvider.symbolOccurrence(filePosition) match {
case Some((so, doc)) =>
implementations(Some(doc), so.symbol, source)
case None =>
List.empty
}
case Right(symbolInformation) =>
implementations(None, symbolInformation.symbol, source)
}
}

private def implementations(
currentDocument: Option[TextDocument],
symbol: String,
source: AbsolutePath
): List[Location] = {
val source = params.getTextDocument.getUri.toAbsolutePath
lazy val global = globalTable.globalSymbolTableFor(source)
val locations = for {
(symbolOccurrence, currentDocument) <- definitionProvider
.symbolOccurence(

// 1. Search locally for symbol
// 2. Search inside workspace
// 3. Search classpath via GlobalSymbolTable
def symbolSearch(symbol: String): Option[SymbolInformation] = {
currentDocument
.flatMap(doc => findSymbol(doc, symbol))
.orElse(findSymbolDef(symbol))
.orElse(global.flatMap(_.safeInfo(symbol)))
}

val dealiased =
if (symbol.desc.isType) dealiasClass(symbol, symbolSearch _) else symbol

val definitionDocument =
currentDocument.flatMap(doc =>
if (doc.definesSymbol(dealiased)) currentDocument
else findSemanticDbForSymbol(dealiased)
)

val inheritanceContext = definitionDocument match {
// symbol is not in workspace, we only search classpath for it
case None =>
globalTable.globalContextFor(
source,
params
implementationsInPath.asScala.toMap
)
.toIterable
} yield {
// 1. Search locally for symbol
// 2. Search inside workspace
// 3. Search classpath via GlobalSymbolTable
def symbolSearch(symbol: String): Option[SymbolInformation] = {
findSymbol(currentDocument, symbol)
.orElse(findSymbolDef(symbol))
.orElse(global.flatMap(_.safeInfo(symbol)))
}
val sym = symbolOccurrence.symbol
val dealiased =
if (sym.desc.isType) dealiasClass(sym, symbolSearch _) else sym

val definitionDocument =
if (currentDocument.definesSymbol(dealiased)) {
Some(currentDocument)
} else {
findSemanticDbForSymbol(dealiased)
}

val inheritanceContext = definitionDocument match {
// symbol is not in workspace, we only search classpath for it
case None =>
globalTable.globalContextFor(
source,
// symbol is in workspace,
// we might need to search different places for related symbols
case Some(textDocument) =>
Some(
InheritanceContext.fromDefinitions(
symbolSearch,
implementationsInPath.asScala.toMap
)
// symbol is in workspace,
// we might need to search different places for related symbols
case Some(textDocument) =>
Some(
InheritanceContext.fromDefinitions(
symbolSearch,
implementationsInPath.asScala.toMap
)
)
}
symbolLocationsFromContext(
dealiased,
source,
inheritanceContext
)
)
}
locations.flatten.toList
symbolLocationsFromContext(
dealiased,
source,
inheritanceContext
).toList
}

def topMethodParents(
symbol: String,
textDocument: TextDocument
): Seq[Location] = {
doc: TextDocument,
symbol: String
): Seq[Either[Location, SymbolInformation]] = {
// location in semanticDB for symbol might not be present when symbol is local then it must be in current document
val textDocument = findSemanticDbForSymbol(symbol).getOrElse(doc)
kpbochenek marked this conversation as resolved.
Show resolved Hide resolved

def findClassInfo(owner: String) = {
if (owner.nonEmpty) {
findSymbol(textDocument, owner)
} else {
textDocument.symbols.find {
case sym =>
sym.signature match {
case sig: ClassSignature =>
sig.declarations.exists(_.symlinks.contains(symbol))
case _ => false
}
textDocument.symbols.find { sym =>
sym.signature match {
case sig: ClassSignature =>
sig.declarations.exists(_.symlinks.contains(symbol))
case _ => false
}
}
}
}

val results = for {
currentInfo <- findSymbol(textDocument, symbol)
if (!isClassLike(currentInfo))
if !isClassLike(currentInfo)
classInfo <- findClassInfo(symbol.owner)
} yield {
classInfo.signature match {
case sig: ClassSignature =>
methodInParentSignature(sig, currentInfo)
case _ => Nil
case classSignature: ClassSignature =>
val globalSymbolTable = globalTable.globalSymbolTableFor(
workspace.resolve(textDocument.uri)
)
methodInParentSignature(
classSignature,
currentInfo,
globalSymbolTable
)
case _ => Seq.empty
}
}
results.getOrElse(Seq.empty)
Expand All @@ -177,13 +207,18 @@ final class ImplementationProvider(
private def methodInParentSignature(
sig: ClassSignature,
childInfo: SymbolInformation,
globalSymbolTable: Option[GlobalSymbolTable],
childASF: Map[String, String] = Map.empty
): Seq[Location] = {
): Seq[Either[Location, SymbolInformation]] = {
sig.parents.flatMap {
case parentSym: TypeRef =>
case parentSym: TypeRef if parentSym.symbol != "scala/AnyRef#" =>
tgodzik marked this conversation as resolved.
Show resolved Hide resolved
val parentTextDocument = findSemanticDbForSymbol(parentSym.symbol)
def search(symbol: String) =
parentTextDocument.flatMap(findSymbol(_, symbol))

def search(symbol: String): Option[SymbolInformation] =
parentTextDocument
.flatMap(findSymbol(_, symbol))
.orElse(globalSymbolTable.flatMap(_.safeInfo(symbol)))

val parentASF =
AsSeenFrom.calculateAsSeenFrom(parentSym, sig.typeParameters)
val asSeenFrom = AsSeenFrom.translateAsSeenFrom(childASF, parentASF)
Expand All @@ -192,6 +227,7 @@ final class ImplementationProvider(
val fromParent = methodInParentSignature(
parenClassSig,
childInfo,
globalSymbolTable,
asSeenFrom
)
if (fromParent.isEmpty) {
Expand All @@ -206,10 +242,10 @@ final class ImplementationProvider(
} else {
fromParent
}
case _ => Nil
case _ => Seq.empty
}

case _ => Nil
case _ => Seq.empty
}
}

Expand All @@ -220,31 +256,34 @@ final class ImplementationProvider(
asSeenFrom: Map[String, String],
search: String => Option[SymbolInformation],
parentTextDocument: Option[TextDocument]
): Option[Location] = {
): Option[Either[Location, SymbolInformation]] = {
val matchingSymbol = MethodImplementation.findParentSymbol(
childInfo,
sig,
parenClassSig,
asSeenFrom,
search
)
for {
symbol <- matchingSymbol
parentDoc <- parentTextDocument
source = workspace.resolve(parentDoc.uri)
implOccurrence <- findDefOccurrence(
parentDoc,
symbol,
source
)
range <- implOccurrence.range
distance = TokenEditDistance.fromBuffer(
source,
parentDoc.text,
buffer
)
revised <- distance.toRevised(range.toLSP)
} yield new Location(source.toNIO.toUri().toString(), revised)
if (matchingSymbol.isDefined && parentTextDocument.isEmpty) {
Some(Right(matchingSymbol.get))
} else {
for {
symbol <- matchingSymbol
parentDoc <- parentTextDocument
source = workspace.resolve(parentDoc.uri)
implOccurrence <- findDefOccurrence(
parentDoc,
symbol.symbol,
source
)
range <- implOccurrence.range
distance = TokenEditDistance.fromBuffer(
source,
parentDoc.text,
buffer
)
revised <- distance.toRevised(range.toLSP)
} yield Left(new Location(source.toNIO.toUri.toString, revised))
}
}

private def symbolLocationsFromContext(
Expand Down Expand Up @@ -328,10 +367,10 @@ final class ImplementationProvider(
loc =>
// we are not interested in local symbols from outside the workspace
(loc.symbol.isLocal && loc.file.isEmpty) ||
// local symbols ineheritance should only be picked up in the same file
// local symbols inheritance should only be picked up in the same file
(loc.symbol.isLocal && loc.file != currentPath)
}
directImplementations.toSet ++ directImplementations
directImplementations ++ directImplementations
kpbochenek marked this conversation as resolved.
Show resolved Hide resolved
.flatMap { loc =>
val allPossible = loop(loc.symbol, loc.file)
allPossible.map(_.translateAsSeenFrom(loc))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@ object MethodImplementation {

def findParentSymbol(
childSymbol: SymbolInformation,
childClassSig: ClassSignature,
parentClassSig: ClassSignature,
asSeenFromMap: Map[String, String],
findSymbol: String => Option[SymbolInformation]
): Option[String] = {
): Option[SymbolInformation] = {
val validMethods = for {
declarations <- parentClassSig.declarations.toIterable
methodSymbol <- declarations.symlinks
Expand All @@ -52,7 +51,7 @@ object MethodImplementation {
if isOverridenMethod(methodSymbolInfo, childSymbol, findParent = true)(
context
)
} yield methodSymbol
} yield methodSymbolInfo
validMethods.headOption
}

Expand Down
Loading