From c2a538d0a6fba26d8f7cac7a39447127ca618cf5 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 14 Feb 2019 22:12:00 +0100 Subject: [PATCH 1/3] IDE: fix signature help for Java and Scala 2 methods Asking for the signature of a method not defined in sources or in Tasty used to throw an exception because the symbol passed to `defPath` must have a span. The call to `defPath` was only used to determine which parameters were implicit, but this can be done in an easier way by checking `isImplicitMethod`, so we can get rid of it at no loss. --- .../dotty/tools/dotc/util/Signatures.scala | 34 +++++++++---------- .../languageserver/SignatureHelpTest.scala | 18 ++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 14dcddcb77dd..96863d3bc495 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -80,27 +80,25 @@ object Signatures { val symbol = denot.symbol val docComment = ParsedComment.docOf(symbol) val classTree = symbol.topLevelClass.asClass.rootTree - val isImplicit: TermName => Boolean = tpd.defPath(symbol, classTree).lastOption match { - case Some(DefDef(_, _, paramss, _, _)) => - val flatParams = paramss.flatten - name => flatParams.find(_.name == name).map(_.symbol.is(Implicit)).getOrElse(false) - case _ => - _ => false + + def toParamss(tp: MethodType)(implicit ctx: Context): List[List[Param]] = { + val rest = tp.resType match { + case res: MethodType => + toParamss(res) + case _ => + Nil + } + tp.paramNames.zip(tp.paramInfos).map { case (name, info) => + Signatures.Param(name.show, + info.widenTermRefExpr.show, + docComment.flatMap(_.paramDoc(name)), + isImplicit = tp.isImplicitMethod) + } :: rest } denot.info.stripPoly match { case tpe: MethodType => - val infos = { - tpe.paramInfoss.zip(tpe.paramNamess).map { case (infos, names) => - infos.zip(names).map { case (info, name) => - Signatures.Param(name.show, - info.widenTermRefExpr.show, - docComment.flatMap(_.paramDoc(name)), - isImplicit = isImplicit(name)) - } - } - } - + val paramss = toParamss(tpe) val typeParams = denot.info match { case poly: PolyType => poly.paramNames.zip(poly.paramInfos).map { case (x, y) => x.show + y.show } @@ -115,7 +113,7 @@ object Signatures { val signature = Signatures.Signature(name, typeParams, - infos, + paramss, returnType, docComment.map(_.mainDoc)) diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index aa90ce570d51..425330b052a9 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -8,6 +8,24 @@ import dotty.tools.dotc.util.Signatures.{Param => P, Signature => S} class SignatureHelpTest { + @Test def javaParam: Unit = { + val signature = + S("codePointAt", Nil, List(List(P("x$0", "Int"))), Some("Int")) + code"""object O { + "hello".codePointAt($m1) + }""".withSource + .signatureHelp(m1, List(signature), Some(0), 0) + } + + @Test def scala2Param: Unit = { + val signature = + S("apply[A]", Nil, List(List(P("xs", "A*"))), Some("List[A]")) + code"""object O { + List($m1) + }""".withSource + .signatureHelp(m1, List(signature), Some(0), 0) + } + @Test def singleParam: Unit = { val signature = S("foo", Nil, List(List(P("param0", "Int"))), Some("Int")) From 804c31c20e43468617c4d464b3f0456d3fb49f10 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 14 Feb 2019 23:10:57 +0100 Subject: [PATCH 2/3] IDE: Don't display parameter lists made of CanBuildFrom in signatures It's a hack, but this should be less confusing for students, and it's only temporary until 2.13 is out. --- .../src/dotty/tools/dotc/util/Signatures.scala | 15 ++++++++++++--- .../tools/languageserver/SignatureHelpTest.scala | 12 ++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 96863d3bc495..cbaad0ee9d5d 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -84,16 +84,25 @@ object Signatures { def toParamss(tp: MethodType)(implicit ctx: Context): List[List[Param]] = { val rest = tp.resType match { case res: MethodType => - toParamss(res) + // HACK: Hide parameter lists consisting only of CanBuildFrom, + // remove this once we switch to the 2.13 standard library. + if (res.resultType.isParameterless && + res.isImplicitMethod && + res.paramInfos.forall(_.classSymbol.fullName.toString == "scala.collection.generic.CanBuildFrom")) + Nil + else + toParamss(res) case _ => Nil } - tp.paramNames.zip(tp.paramInfos).map { case (name, info) => + val params = tp.paramNames.zip(tp.paramInfos).map { case (name, info) => Signatures.Param(name.show, info.widenTermRefExpr.show, docComment.flatMap(_.paramDoc(name)), isImplicit = tp.isImplicitMethod) - } :: rest + } + + params :: rest } denot.info.stripPoly match { diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index 425330b052a9..06c7b9dd98d6 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -8,7 +8,7 @@ import dotty.tools.dotc.util.Signatures.{Param => P, Signature => S} class SignatureHelpTest { - @Test def javaParam: Unit = { + @Test def fromJava: Unit = { val signature = S("codePointAt", Nil, List(List(P("x$0", "Int"))), Some("Int")) code"""object O { @@ -17,13 +17,17 @@ class SignatureHelpTest { .signatureHelp(m1, List(signature), Some(0), 0) } - @Test def scala2Param: Unit = { - val signature = + @Test def fromScala2: Unit = { + val applySig = S("apply[A]", Nil, List(List(P("xs", "A*"))), Some("List[A]")) + val mapSig = + S("map[B, That]", Nil, List(List(P("f", "A => B"))), Some("That")) code"""object O { List($m1) + List(1, 2, 3).map($m2) }""".withSource - .signatureHelp(m1, List(signature), Some(0), 0) + .signatureHelp(m1, List(applySig), Some(0), 0) + .signatureHelp(m2, List(mapSig), Some(0), 0) } @Test def singleParam: Unit = { From 37ec52c2e0b371f9e8f475d60b5893ca15fb7e95 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 15 Feb 2019 00:13:50 +0100 Subject: [PATCH 3/3] Also hide implicit parameter lists made of DummyImplicit This is more legitimate than hiding CanBuildFrom since there's never any reason to pass a DummyImplicit instance explicitly, and DummyImplicit is used in the 2.13 standard library to avoid erased signature clashes between overloads. --- .../dotty/tools/dotc/core/Definitions.scala | 8 +++++- .../dotty/tools/dotc/util/Signatures.scala | 8 ++++-- .../languageserver/SignatureHelpTest.scala | 28 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index d3b9b655b65f..ff380a4b534d 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -355,12 +355,18 @@ class Definitions { lazy val Predef_undefinedR: TermRef = ScalaPredefModule.requiredMethodRef(nme.???) def Predef_undefined(implicit ctx: Context): Symbol = Predef_undefinedR.symbol - def SubTypeClass(implicit ctx: Context): Symbol = + def SubTypeClass(implicit ctx: Context): ClassSymbol = if (isNewCollections) ctx.requiredClass("scala.<:<") else ScalaPredefModule.requiredClass("<:<") + def DummyImplicitClass(implicit ctx: Context): ClassSymbol = + if (isNewCollections) + ctx.requiredClass("scala.DummyImplicit") + else + ScalaPredefModule.requiredClass("DummyImplicit") + lazy val ScalaRuntimeModuleRef: TermRef = ctx.requiredModuleRef("scala.runtime.ScalaRunTime") def ScalaRuntimeModule(implicit ctx: Context): Symbol = ScalaRuntimeModuleRef.symbol def ScalaRuntimeClass(implicit ctx: Context): ClassSymbol = ScalaRuntimeModule.moduleClass.asClass diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index cbaad0ee9d5d..e782aeb43e84 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -84,11 +84,13 @@ object Signatures { def toParamss(tp: MethodType)(implicit ctx: Context): List[List[Param]] = { val rest = tp.resType match { case res: MethodType => - // HACK: Hide parameter lists consisting only of CanBuildFrom, - // remove this once we switch to the 2.13 standard library. + // Hide parameter lists consisting only of CanBuildFrom or DummyImplicit, + // we can remove the CanBuildFrom special-case once we switch to the 2.13 standard library. if (res.resultType.isParameterless && res.isImplicitMethod && - res.paramInfos.forall(_.classSymbol.fullName.toString == "scala.collection.generic.CanBuildFrom")) + res.paramInfos.forall(info => + info.classSymbol.fullName.toString == "scala.collection.generic.CanBuildFrom" || + info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass))) Nil else toParamss(res) diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index 06c7b9dd98d6..68fc0ebcefec 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -30,6 +30,34 @@ class SignatureHelpTest { .signatureHelp(m2, List(mapSig), Some(0), 0) } + /** Implicit parameter lists consisting solely of DummyImplicits are hidden. */ + @Test def hiddenDummyParams: Unit = { + val foo1Sig = + S("foo1", Nil, List(List(P("param0", "Int"))), Some("Int")) + val foo2Sig = + S("foo2", Nil, List(List(P("param0", "Int"))), Some("Int")) + val foo3Sig = + S("foo3", Nil, List(List(P("param0", "Int")), + List(P("dummy", "DummyImplicit"))), Some("Int")) + val foo4Sig = + S("foo4", Nil, List(List(P("param0", "Int")), + List(P("x", "Int", isImplicit = true), P("dummy", "DummyImplicit", isImplicit = true))), Some("Int")) + code"""object O { + def foo1(param0: Int)(implicit dummy: DummyImplicit): Int = ??? + def foo2(param0: Int)(implicit dummy1: DummyImplicit, dummy2: DummyImplicit): Int = ??? + def foo3(param0: Int)(dummy: DummyImplicit): Int = ??? + def foo4(param0: Int)(implicit x: Int, dummy: DummyImplicit): Int = ??? + foo1($m1) + foo2($m2) + foo3($m3) + foo4($m4) + }""".withSource + .signatureHelp(m1, List(foo1Sig), Some(0), 0) + .signatureHelp(m2, List(foo2Sig), Some(0), 0) + .signatureHelp(m3, List(foo3Sig), Some(0), 0) + .signatureHelp(m4, List(foo4Sig), Some(0), 0) + } + @Test def singleParam: Unit = { val signature = S("foo", Nil, List(List(P("param0", "Int"))), Some("Int"))