diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/CompletionProvider.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/CompletionProvider.scala index b775b066535..120d237e50a 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/CompletionProvider.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/CompletionProvider.scala @@ -99,6 +99,8 @@ class CompletionProvider( d.label case o: TextEditMember => o.label.getOrElse(labelWithSig) + case _: WorkspaceImplicitMember => + s"$labelWithSig (implicit)" case o: WorkspaceMember => s"$ident - ${o.sym.owner.fullName}" case _ => labelWithSig @@ -152,6 +154,31 @@ class CompletionProvider( item.setAdditionalTextEdits(i.autoImports.asJava) case d: DependecyMember => item.setTextEdit(d.edit) + case m: WorkspaceImplicitMember => + val impPos = importPosition.getOrElse(AutoImportPosition(0, 0, false)) + val suffix = + if ( + clientSupportsSnippets && m.sym.paramss.headOption.exists( + _.nonEmpty + ) + ) "($0)" + else "" + val (short, edits) = ShortenedNames.synthesize( + TypeRef( + ThisType(m.sym.owner), + m.sym, + Nil + ), + pos, + context, + impPos + ) + val edit: l.TextEdit = textEdit( + short + suffix, + editRange + ) + item.setTextEdit(edit) + item.setAdditionalTextEdits(edits.asJava) case w: WorkspaceMember => def createTextEdit(identifier: String) = textEdit(w.wrap(identifier), w.editRange.getOrElse(editRange)) @@ -360,16 +387,46 @@ class CompletionProvider( text, isAmmoniteScript ) + val searchResults = if (kind == CompletionListKind.Scope) { workspaceSymbolListMembers(query, pos, visit) } else { - SymbolSearch.Result.COMPLETE + typedTreeAt(pos) match { + case Select(qualifier, _) + if qualifier.tpe != null && !qualifier.tpe.isError => + workspaceExtensionMethods(query, pos, visit, qualifier.tpe) + case _ => SymbolSearch.Result.COMPLETE + } } InterestingMembers(buf.result(), searchResults) } + private def workspaceExtensionMethods( + query: String, + pos: Position, + visit: Member => Boolean, + selectType: Type + ): SymbolSearch.Result = { + val context = doLocateContext(pos) + val visitor = new CompilerSearchVisitor( + context, + sym => + if (sym.safeOwner.isImplicit) { + val ownerConstructor = sym.owner.info.member(nme.CONSTRUCTOR) + def typeParams = sym.owner.info.typeParams + ownerConstructor.info.paramss match { + case List(List(param)) + if selectType <:< boundedWildcardType(param.info, typeParams) => + visit(new WorkspaceImplicitMember(sym)) + case _ => false + } + } else false + ) + search.searchMethods(query, buildTargetIdentifier, visitor) + } + private def isFunction(symbol: Symbol): Boolean = { compiler.definitions.isFunctionSymbol( symbol.info.finalResultType.typeSymbol diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/MetalsGlobal.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/MetalsGlobal.scala index b9e00e00906..bbc23321fe8 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/MetalsGlobal.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/MetalsGlobal.scala @@ -361,6 +361,13 @@ class MetalsGlobal( args.map(arg => loop(arg, None)) ) } + } else if (sym.isMethod && sym.safeOwner.isImplicit) { + history.tryShortenName(ShortName(sym.safeOwner)) + TypeRef( + NoPrefix, + shortSymbol, + args.map(arg => loop(arg, None)) + ) } else { TypeRef( loop(pre, Some(ShortName(sym))), @@ -1072,4 +1079,35 @@ class MetalsGlobal( } } + /** + * Creates a bounded wildcard type for a type of parameter + * using information about type parameters. + * + * E.g. for class A[T](x: List[T]) + * List[Int] <:< List[T] is false, + * this method for List[T] will return List[_ >: Nothing <: Any], + * and List[Int] <:< List[_ >: Nothing <: Any] is true. + */ + def boundedWildcardType( + tpe: Type, + typeParams: List[Symbol] + ): Type = { + if (typeParams.isEmpty) tpe + else { + typeParams.find(_ == tpe.typeSymbol) match { + case Some(tpeDef) => + tpeDef.info match { + case bounds: TypeBounds => BoundedWildcardType(bounds) + case tpe => tpe + } + case None => + tpe match { + case TypeRef(pre, sym, args) => + TypeRef(pre, sym, args.map(boundedWildcardType(_, typeParams))) + case t => t + } + } + } + } + } diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/Signatures.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/Signatures.scala index eb2c1b49c5c..b589fa73d67 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/Signatures.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/Signatures.scala @@ -189,7 +189,7 @@ trait Signatures { compiler: MetalsGlobal => sym.isStaticMember || // Java static sym.owner.ownerChain.forall { s => // ensure the symbol can be referenced in a static manner, without any instance - s.isPackageClass || s.isPackageObjectClass || s.isModule + s.isPackageClass || s.isPackageObjectClass || s.isModule || s.isModuleClass } ) { history(name) = short diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/completions/Completions.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/completions/Completions.scala index 3a586d82c0c..d67dd77080b 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/completions/Completions.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/completions/Completions.scala @@ -41,6 +41,9 @@ trait Completions { this: MetalsGlobal => def editRange: Option[l.Range] = None } + class WorkspaceImplicitMember(sym: Symbol) + extends ScopeMember(sym, sym.tpe, true, EmptyTree) + class WorkspaceInterpolationMember( sym: Symbol, override val additionalTextEdits: List[l.TextEdit], diff --git a/tests/cross/src/main/scala/tests/BaseCompletionSuite.scala b/tests/cross/src/main/scala/tests/BaseCompletionSuite.scala index ea206c63371..6f8fecd0bc8 100644 --- a/tests/cross/src/main/scala/tests/BaseCompletionSuite.scala +++ b/tests/cross/src/main/scala/tests/BaseCompletionSuite.scala @@ -22,6 +22,7 @@ abstract class BaseCompletionSuite extends BasePCSuite { private def resolvedCompletions( params: CompilerOffsetParams ): CompletionList = { + presentationCompiler.restart() val result = presentationCompiler.complete(params).get() val newItems = result.getItems.asScala.map { item => item.data diff --git a/tests/cross/src/test/scala/tests/pc/CompletionExtensionMethodSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionExtensionMethodSuite.scala index 269053fc3eb..14f8a21184f 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionExtensionMethodSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionExtensionMethodSuite.scala @@ -4,11 +4,8 @@ import tests.BaseCompletionSuite class CompletionExtensionMethodSuite extends BaseCompletionSuite { - override def ignoreScalaVersion: Option[IgnoreScalaVersion] = - Some(IgnoreScala2) - check( - "simple", + "simple".tag(IgnoreScala2), """|package example | |object enrichments: @@ -25,18 +22,23 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "simple-old-syntax", """|package example | - |object Test: - | implicit class TestOps(a: Int): + |object Test { + | implicit class TestOps(a: Int) { | def testOps(b: Int): String = ??? + | } + |} | - |def main = 100.test@@ + |object O{ + | def main = 100.test@@ + |} |""".stripMargin, """|testOps(b: Int): String (implicit) - |""".stripMargin + |""".stripMargin, + filter = _.contains("(implicit)") ) check( - "simple2", + "simple2".tag(IgnoreScala2), """|package example | |object enrichments: @@ -54,11 +56,15 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "simple2-old-syntax", """|package example | - |object enrichments: - | implicit class TestOps(a: Int): + |object enrichments { + | implicit class TestOps(a: Int) { | def testOps(b: Int): String = ??? + | } + |} | - |def main = 100.t@@ + |object O { + | def main = 100.t@@ + |} |""".stripMargin, """|testOps(b: Int): String (implicit) |""".stripMargin, @@ -66,7 +72,7 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { ) check( - "simple-empty", + "simple-empty".tag(IgnoreScala2), """|package example | |object enrichments: @@ -84,11 +90,15 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "simple-empty-old", """|package example | - |object enrichments: - | implicit class TestOps(a: Int): + |object enrichments { + | implicit class TestOps(a: Int) { | def testOps(b: Int): String = ??? + | } + |} | - |def main = 100.@@ + |object O { + | def main = 100.@@ + |} |""".stripMargin, """|testOps(b: Int): String (implicit) |""".stripMargin, @@ -96,7 +106,7 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { ) check( - "filter-by-type", + "filter-by-type".tag(IgnoreScala2), """|package example | |object enrichments: @@ -116,21 +126,26 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "filter-by-type-old", """|package example | - |object enrichments: - | implicit class A(num: Int): + |object enrichments { + | implicit class A(num: Int) { | def identity2: Int = num + 1 - | implicit class B(str: String): + | } + | implicit class B(str: String) { | def identity: String = str + | } + |} | - |def main = "foo".iden@@ + |object O { + | def main = "foo".iden@@ + |} |""".stripMargin, """|identity: String (implicit) - |""".stripMargin // identity2 won't be available - + |""".stripMargin, // identity2 won't be available + filter = _.contains("(implicit)") ) check( - "filter-by-type-subtype", + "filter-by-type-subtype".tag(IgnoreScala2), """|package example | |class A @@ -154,11 +169,15 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { |class A |class B extends A | - |object enrichments: - | implicit class Test(a: A): + |object enrichments { + | implicit class Test(a: A) { | def doSomething: A = a + | } + |} | - |def main = (new B).do@@ + |object O { + | def main = (new B).do@@ + |} |""".stripMargin, """|doSomething: A (implicit) |""".stripMargin, @@ -166,7 +185,7 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { ) checkEdit( - "simple-edit", + "simple-edit".tag(IgnoreScala2), """|package example | |object enrichments: @@ -191,26 +210,35 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "simple-edit-old", """|package example | - |object enrichments: - | implicit class A (num: Int): + |object enrichments { + | implicit class A (num: Int) { | def incr: Int = num + 1 + | } + |} | - |def main = 100.inc@@ + |object O { + | def main = 100.inc@@ + |} |""".stripMargin, """|package example | |import example.enrichments.A | - |object enrichments: - | implicit class A (num: Int): + |object enrichments { + | implicit class A (num: Int) { | def incr: Int = num + 1 + | } + |} | - |def main = 100.incr - |""".stripMargin + |object O { + | def main = 100.incr + |} + |""".stripMargin, + filter = _.contains("(implicit)") ) checkEdit( - "simple-edit-suffix", + "simple-edit-suffix".tag(IgnoreScala2), """|package example | |object enrichments: @@ -232,7 +260,7 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { ) checkEdit( - "name-conflict", + "name-conflict".tag(IgnoreScala2), """|package example | |import example.enrichments.* @@ -265,28 +293,38 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "simple-edit-suffix-old", """|package example | - |object enrichments: - | implicit class A (val num: Int): + |object enrichments { + | implicit class A (val num: Int) { | def plus(other: Int): Int = num + other + | } + |} | - |def main = 100.pl@@ + |object O { + | def main = 100.pl@@ + |} |""".stripMargin, """|package example | |import example.enrichments.A | - |object enrichments: - | implicit class A (val num: Int): + |object enrichments { + | implicit class A (val num: Int) { | def plus(other: Int): Int = num + other + | } + |} | - |def main = 100.plus($0) + |object O { + | def main = 100.plus($0) + |} |""".stripMargin ) // NOTE: In 3.1.3, package object name includes the whole path to file // eg. in 3.2.2 we get `A$package`, but in 3.1.3 `/some/path/to/file/A$package` check( - "directly-in-pkg1".tag(IgnoreScalaVersion.forLessThan("3.2.2")), + "directly-in-pkg1".tag( + IgnoreScalaVersion.forLessThan("3.2.2") + ), """| |package example: | extension (num: Int) @@ -305,19 +343,24 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { IgnoreScalaVersion.forLessThan("3.2.2") ), """| - |package examples: - | implicit class A(num: Int): + |package examples { + | implicit class A(num: Int) { | def incr: Int = num + 1 + | } + |} | - |package examples2: + |package examples2 { | def main = 100.inc@@ + |} |""".stripMargin, """|incr: Int (implicit) |""".stripMargin ) check( - "directly-in-pkg2".tag(IgnoreScalaVersion.forLessThan("3.2.2")), + "directly-in-pkg2".tag( + IgnoreScalaVersion.forLessThan("3.2.2") + ), """|package example: | object X: | def fooBar(num: Int) = num + 1 @@ -333,22 +376,30 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { check( "directly-in-pkg2-old" .tag( - IgnoreScalaVersion.forLessThan("3.2.2") + IgnoreScalaVersion.for3LessThan("3.2.2") ), - """|package examples: - | object X: + """|package examples { + | object X { | def fooBar(num: Int) = num + 1 + | } | implicit class A (num: Int) { def incr: Int = num + 1 } + |} | - |package examples2: - | def main = 100.inc@@ + |package examples2 { + | object O { + | def main = 100.inc@@ + | } + |} |""".stripMargin, """|incr: Int (implicit) - |""".stripMargin + |""".stripMargin, + filter = _.contains("(implicit)") ) checkEdit( - "directly-in-pkg3".tag(IgnoreScalaVersion.forLessThan("3.2.2")), + "directly-in-pkg3".tag( + IgnoreScalaVersion.forLessThan("3.2.2") + ), """|package example: | extension (num: Int) def incr: Int = num + 1 | @@ -367,21 +418,44 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { checkEdit( "directly-in-pkg3-old" .tag( - IgnoreScalaVersion.forLessThan("3.2.2") + IgnoreScalaVersion.for3LessThan("3.2.2") ), - """|package examples: + """|package examples { | implicit class A (num: Int) { def incr: Int = num + 1 } + |} | - |package examples2: - | def main = 100.inc@@ + |package examples2 { + | object O { + | def main = 100.inc@@ + | } + |} |""".stripMargin, """|import examples.A - |package examples: + |package examples { | implicit class A (num: Int) { def incr: Int = num + 1 } + |} | - |package examples2: - | def main = 100.incr - |""".stripMargin + |package examples2 { + | object O { + | def main = 100.incr + | } + |} + |""".stripMargin, + compat = Map( + "2" -> """|package examples { + | implicit class A (num: Int) { def incr: Int = num + 1 } + |} + | + |package examples2 { + | + | import examples.A + | object O { + | def main = 100.incr + | } + |} + |""".stripMargin + ), + filter = _.contains("(implicit)") ) check( @@ -405,18 +479,25 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { check( "nested-pkg-old" .tag( - IgnoreScalaVersion.forLessThan("3.2.2") + IgnoreScalaVersion.for3LessThan("3.2.2") ), - """|package aa: // some comment - | package cc: - | implicit class A (num: Int): + """|package aa { // some comment + | package cc { + | implicit class A (num: Int){ | def increment2 = num + 2 - | implicit class A (num: Int): + | } + | } + | implicit class A (num: Int) { | def increment = num + 1 + | } + |} | | - |package bb: - | def main: Unit = 123.incre@@ + |package bb { + | object O { + | def main: Unit = 123.incre@@ + | } + |} |""".stripMargin, """|increment: Int (implicit) |increment2: Int (implicit) @@ -427,41 +508,119 @@ class CompletionExtensionMethodSuite extends BaseCompletionSuite { "implicit-val-var".tag(IgnoreForScala3CompilerPC), """|package example | - |object Test: - | implicit class TestOps(val testArg: Int): + |object Test { + | implicit class TestOps(val testArg: Int) { | var testVar: Int = 42 | val testVal: Int = 42 | def testOps(b: Int): String = ??? + | } + |} | - |def main = 100.test@@ + |object O { + | def main = 100.test@@ + |} |""".stripMargin, """|testArg: Int (implicit) |testVal: Int (implicit) |testVar: Int (implicit) |testOps(b: Int): String (implicit) - |""".stripMargin + |""".stripMargin, + compat = Map( + "2" -> + """|testArg: Int (implicit) + |testOps(b: Int): String (implicit) + |testVal: Int (implicit) + |testVar: Int (implicit) + |""".stripMargin + ), + filter = _.contains("(implicit)") ) checkEdit( "implicit-val-edit".tag(IgnoreForScala3CompilerPC), """|package example | - |object Test: - | implicit class TestOps(a: Int): + |object Test { + | implicit class TestOps(a: Int) { | val testVal: Int = 42 + | } + |} | - |def main = 100.test@@ + |object O { + | def main = 100.test@@ + |} |""".stripMargin, """|package example | |import example.Test.TestOps | - |object Test: - | implicit class TestOps(a: Int): + |object Test { + | implicit class TestOps(a: Int) { | val testVal: Int = 42 + | } + |} | - |def main = 100.testVal - |""".stripMargin + |object O { + | def main = 100.testVal + |} + |""".stripMargin, + filter = _.contains("(implicit)") + ) + + check( + "complex-type-old", + """|package example + | + |object Test { + | implicit class TestOps(a: List[Int]) { + | def testOps(b: Int) = ??? + | } + |} + | + |object ActualTest { + | List(1).tes@@ + |} + |""".stripMargin, + "testOps(b: Int): Nothing (implicit)", + filter = _.contains("(implicit)") + ) + + check( + "complex-type-old2".tag(IgnoreScala3), + """|package example + | + |object Test { + | implicit class TestOps[T](a: List[T]) { + | def testOps(b: Int) = ??? + | } + |} + | + |object ActualTest { + | List(1).tes@@ + |} + |""".stripMargin, + "testOps(b: Int): Nothing (implicit)", + filter = _.contains("(implicit)") + ) + + check( + "complex-type-old3".tag(IgnoreScala3), + """|package example + | + |case class A[-T](t: T) + | + |object Test { + | implicit class TestOps[T](a: A[T]) { + | def testOps(b: Int) = ??? + | } + |} + | + |object ActualTest { + | A(1).tes@@ + |} + |""".stripMargin, + "testOps(b: Int): Nothing (implicit)", + filter = _.contains("(implicit)") ) } diff --git a/tests/cross/src/test/scala/tests/pc/CompletionIssueSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionIssueSuite.scala index 2564adcb130..821ea80a687 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionIssueSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionIssueSuite.scala @@ -95,29 +95,17 @@ class CompletionIssueSuite extends BaseCompletionSuite { | NestedLea@@ |}""".stripMargin, """|package a + | + |import a.A.Nested.NestedLeaf |object A { | object Nested{ | object NestedLeaf | } |} |object B { - | A.Nested.NestedLeaf + | NestedLeaf |} - |""".stripMargin, - compat = Map( - "3" -> """|package a - | - |import a.A.Nested.NestedLeaf - |object A { - | object Nested{ - | object NestedLeaf - | } - |} - |object B { - | NestedLeaf - |} - |""".stripMargin - ) + |""".stripMargin ) checkEdit( @@ -146,6 +134,7 @@ class CompletionIssueSuite extends BaseCompletionSuite { | Sweden, | USA |} + |import all.World.Countries.Norway | |object World { | object Countries{ @@ -157,32 +146,9 @@ class CompletionIssueSuite extends BaseCompletionSuite { |} |import all.World.Countries.France |object B { - | val allCountries = Sweden + France + USA + World.Countries.Norway + | val allCountries = Sweden + France + USA + Norway |} - |""".stripMargin, - compat = Map( - "3" -> - """|package all - |import all.World.Countries.{ - | Sweden, - | USA - |} - |import all.World.Countries.Norway - | - |object World { - | object Countries{ - | object Sweden - | object Norway - | object France - | object USA - | } - |} - |import all.World.Countries.France - |object B { - | val allCountries = Sweden + France + USA + Norway - |} - |""".stripMargin - ) + |""".stripMargin ) check( diff --git a/tests/cross/src/test/scala/tests/pc/CompletionSnippetSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionSnippetSuite.scala index 2d3c0ce0b17..87ee72600fa 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionSnippetSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionSnippetSuite.scala @@ -219,7 +219,7 @@ class CompletionSnippetSuite extends BaseCompletionSuite { "2.13" -> """|Iterable |Iterable[$0] {} - |IterableOnce[$0] {} + |IterableOnce[$0] |""".stripMargin, "3" -> """|Iterable[$0] {} diff --git a/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala index 8d81dfb63de..b51d0fcf4ff 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala @@ -380,11 +380,8 @@ class CompletionSuite extends BaseCompletionSuite { | } | Xtension@@ |}""".stripMargin, - """|XtensionMethod(a: Int): A.XtensionMethod - |""".stripMargin, - compat = Map( - "3" -> "XtensionMethod(a: Int): XtensionMethod" - ) + """|XtensionMethod(a: Int): XtensionMethod + |""".stripMargin ) check( @@ -1793,7 +1790,7 @@ class CompletionSuite extends BaseCompletionSuite { | val t: TT@@ |} |""".stripMargin, - "TTT[A] = O.TTT", + "TTT[A] = TTT", compat = Map( "3" -> "TTT[A <: Int] = List[A]" ) diff --git a/tests/cross/src/test/scala/tests/pc/CompletionWorkspaceSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionWorkspaceSuite.scala index ca826152f28..e20e791fded 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionWorkspaceSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionWorkspaceSuite.scala @@ -652,20 +652,12 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite { | Implicits@@ |} |""".stripMargin, - """|import scala.concurrent.ExecutionContext + """|import scala.concurrent.ExecutionContext.Implicits |object Main { - | ExecutionContext.Implicits + | Implicits |} |""".stripMargin, - filter = _ == "Implicits - scala.concurrent.ExecutionContext", - compat = Map { - "3" -> - """|import scala.concurrent.ExecutionContext.Implicits - |object Main { - | Implicits - |} - |""".stripMargin - } + filter = _ == "Implicits - scala.concurrent.ExecutionContext" ) // this test was intended to check that import is rendered correctly - without `$` symbol @@ -844,9 +836,9 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite { | |package b { | - | import a.A + | import a.A.Beta | object B{ - | val x: A.Beta + | val x: Beta | } |} |""".stripMargin, @@ -1082,4 +1074,31 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite { |""".stripMargin, "" ) + + check( + "implicit-class", + """|package example + |object Test { + | implicit class TestOps(a: Int) { + | def testOps(b: Int) = ??? + | } + | implicit class TestOps2(a: String) { + | def testOps2(b: Int) = ??? + | } + | implicit class TestOps4[A](a: List[A]) { + | def testOps4(b: Int) = ??? + | } + | class TestOps3(a: String) { + | def testOps3(b: Int) = ??? + | } + |} + | + |object ActualTest { + | 1.tes@@ + |} + |""".stripMargin, + "testOps(b: Int): Nothing (implicit)", + filter = _.contains("testOps") + ) + }