Skip to content

Commit

Permalink
bugfix: completions for type companion object
Browse files Browse the repository at this point in the history
  • Loading branch information
kasiaMarek committed Jun 28, 2023
1 parent ba5edd8 commit 07bf459
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scala.meta.internal.pc

import scala.util.control.NonFatal

import scala.meta.internal.mtags.MtagsEnrichments.decoded
import scala.meta.internal.mtags.MtagsEnrichments.stripBackticks

import dotty.tools.dotc.core.Contexts.*
Expand Down Expand Up @@ -49,7 +50,29 @@ object SemanticdbSymbols:
typeSym :: owner.info.decl(termName(value)).symbol :: Nil
else typeSym :: Nil
case Descriptor.Term(value) =>
owner.info.decl(termName(value)).symbol :: Nil
val outSymbol = owner.info.decl(termName(value)).symbol
if outSymbol.exists
then outSymbol :: Nil
else if owner.is(Package)
then
// Fallback for type companion objects,
// e.g.
// ```File.scala
// package a
// type Cow = Int
// object Cow
// ```
// `ScalaTopLevelMtags` emits `a/Cow.`
// but the symbol we look for is `a/File$package/Cow.`
// (look: tests.pc.CompletionWorkspaceSuite.type-apply)
owner.info.decls
.filter { s =>
s.isPackageObject && s.name.decoded.endsWith("$package")
}
.flatMap(tryMember)
.toList
else Nil
end if
case Descriptor.Package(value) =>
owner.info.decl(termName(value)).symbol :: Nil
case Descriptor.Parameter(value) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class ScalaToplevelMtags(
} else region
data.token match {
case PACKAGE =>
val isNotPackageObject = emitPackage(currRegion)
val isNotPackageObject = emitPackage(currRegion.owner)
if (isNotPackageObject) {
val nextRegion = new Region.Package(currentOwner, currRegion)
loop(indent, false, nextRegion, newExpectPkgTemplate)
Expand Down Expand Up @@ -190,7 +190,7 @@ class ScalaToplevelMtags(
newExpectExtensionTemplate(nextOwner)
)
case CLASS | TRAIT | OBJECT | ENUM if needEmitMember(currRegion) =>
emitMember(currRegion, false)
emitMember(false, currRegion.owner)
val template = expectTemplate match {
case Some(expect) if expect.isCaseClassConstructor =>
newExpectCaseClassTemplate
Expand Down Expand Up @@ -442,20 +442,20 @@ class ScalaToplevelMtags(
} else ()
}

def emitPackage(region: Region): Boolean = {
def emitPackage(owner: String): Boolean = {
require(scanner.curr.token == PACKAGE, "package")
if (currentOwner eq Symbols.EmptyPackage) {
currentOwner = Symbols.RootPackage
}
currentOwner = region.owner
currentOwner = owner
acceptTrivia()
scanner.curr.token match {
case IDENTIFIER =>
val paths = parsePath()
paths.foreach { path => pkg(path.name, path.pos) }
true
case OBJECT =>
emitMember(region, isPackageObject = true)
emitMember(isPackageObject = true, owner)
false
case _ =>
require(isOk = false, "package name or package object")
Expand Down Expand Up @@ -485,11 +485,11 @@ class ScalaToplevelMtags(
/**
* Enters a toplevel symbol such as class, trait or object
*/
def emitMember(region: Region, isPackageObject: Boolean): Unit = {
def emitMember(isPackageObject: Boolean, owner: String): Unit = {
val kind = scanner.curr.token
acceptTrivia()
val maybeName = newIdentifier
currentOwner = region.owner
currentOwner = owner
maybeName.foreach { name =>
kind match {
case CLASS | ENUM =>
Expand All @@ -501,9 +501,6 @@ class ScalaToplevelMtags(
currentOwner = symbol(Scala.Descriptor.Package(name.name))
term("package", name.pos, Kind.OBJECT, 0)
} else {
if (region.isCompanionForType(name.name)) {
currentOwner = region.termOwner
}
term(name.name, name.pos, Kind.OBJECT, 0)
}
}
Expand Down Expand Up @@ -540,7 +537,6 @@ class ScalaToplevelMtags(
})
case TYPE =>
newIdentifier.foreach { name =>
region.onEmitType(name.name)
tpe(name.name, name.pos, Kind.TYPE, 0)
}
case DEF =>
Expand Down Expand Up @@ -833,11 +829,6 @@ object ScalaToplevelMtags {
val withTermOwner: String => Region = _ => this
def emitIdentifier: Boolean = false
val changeCaseClassState: Boolean => Region = _ => this
private var emittedTopLevelTypes = List.empty[String]
def onEmitType(sym: String): Unit =
if (owner.isPackage) emittedTopLevelTypes = sym :: emittedTopLevelTypes
def isCompanionForType(sym: String): Boolean =
emittedTopLevelTypes.contains(sym)
}

object Region {
Expand Down
17 changes: 17 additions & 0 deletions tests/cross/src/test/scala/tests/pc/CompletionWorkspaceSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1003,4 +1003,21 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite {
|MyType - demo.other.A$package""".stripMargin,
)

check(
"type-apply2".tag(IgnoreScalaVersion.forLessThan("3.2.2")),
"""|package demo
|
|package other {
| object MyType {
| def apply(m: Long): MyType = m
| }
| type MyType = Long
|}
|
|val j = MyTy@@
|""".stripMargin,
"""|MyType(m: Long): MyType
|MyType - demo.other.A$package""".stripMargin,
)

}
4 changes: 2 additions & 2 deletions tests/unit/src/test/scala/tests/ScalaToplevelSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,8 @@ class ScalaToplevelSuite extends BaseSuite {
| def apply(m: Long): Cow = m
|
|""".stripMargin,
List("s/", "s/Test$package.", "s/Test$package.Cow#", "s/Test$package.Cow.",
"s/Test$package.Cow.apply()."),
List("s/", "s/Test$package.", "s/Test$package.Cow#", "s/Cow.",
"s/Cow.apply()."),
dialect = dialects.Scala3,
all = true,
)
Expand Down

0 comments on commit 07bf459

Please sign in to comment.