diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index b00cd1036018..92326b0ffeb0 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -281,7 +281,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { if (t.symbol.exists && t.hasType) { if (!t.symbol.isCompleted) t.symbol.info = UnspecifiedErrorType t.symbol.annotations.foreach { annot => - /* In some cases annotations are are used on themself (possibly larger cycles). + /* In some cases annotations are used on themself (possibly larger cycles). * This is the case with the java.lang.annotation.Target annotation, would end * in an infinite loop while cleaning. The `seen` is added to ensure that those * trees are not cleand twice. diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index ce05cfb40294..36d1723b9c06 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -1,13 +1,17 @@ package dotty.tools.dotc package util +import dotty.tools.dotc.ast.NavigateAST +import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.core.NameOps.* +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Symbols.defn + import ast.Trees.* import ast.tpd -import core.Constants.Constant import core.Contexts.* import core.Denotations.{SingleDenotation, Denotation} import core.Flags -import core.NameOps.isUnapplyName import core.Names.* import core.NameKinds import core.Types.* @@ -31,54 +35,56 @@ object Signatures { */ case class Signature( name: String, - tparams: List[String], paramss: List[List[Param]], returnType: Option[String], doc: Option[String] = None, denot: Option[SingleDenotation] = None ) + sealed trait Param: + def show: String + val doc: Option[String] = None + /** * Represent a method's parameter. * - * @param name The name of the parameter - * @param tpe The type of the parameter - * @param doc The documentation of this parameter - * @param isImplicit Is this parameter implicit? + * @param name The name of the parameter + * @param tpe The type of the parameter + * @param doc The documentation of this parameter + * @param isImplicit Is this parameter implicit? + * @param isReordered Is the parameter reordered in its parameter list? */ - case class Param(name: String, tpe: String, doc: Option[String] = None, isImplicit: Boolean = false) { - def show: String = if name.nonEmpty then s"$name: $tpe" else tpe - } + case class MethodParam( + name: String, + tpe: String, + override val doc: Option[String] = None, + isImplicit: Boolean = false, + isReordered: Boolean = false + ) extends Param: + def show: String = if name.nonEmpty && !isReordered then s"$name: $tpe" + else if name.nonEmpty then s"[$name: $tpe]" + else tpe /** - * Extract (current parameter index, function index, functions) method call for given position. - * - * @param path The path to the function application - * @param pos The position of the cursor + * Represent a type parameter. * - * @return A triple containing the index of the parameter being edited, the index of functeon - * being called, the list of overloads of this function). + * @param tpe The type of the parameter + * @param doc The documentation of this parameter */ - def signatureHelp(path: List[tpd.Tree], pos: Span)(using Context): (Int, Int, List[Signature]) = - computeSignatureHelp(path, pos) + case class TypeParam(tpe: String, override val doc: Option[String] = None) extends Param: + def show = tpe /** - * Extract (current parameter index, function index, functions) out of a method call. + * Extract (current parameter index, function index, functions) method call for given position. * * @param path The path to the function application - * @param span The position of the cursor + * @param pos The position of the cursor * - * @return A triple containing the index of the parameter being edited, the index of the function + * @return A triple containing the index of the parameter being edited, the index of function * being called, the list of overloads of this function). */ - @deprecated( - """This method is deprecated in favor of `signatureHelp`. - Returned denotation cannot be distinguished if its unapply or apply context""", - "3.1.3" - ) - def callInfo(path: List[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) = - val (paramN, funN, signatures) = computeSignatureHelp(path, span) - (paramN, funN, signatures.flatMap(_.denot)) + def signatureHelp(path: List[tpd.Tree], pos: Span)(using Context): (Int, Int, List[Signature]) = + computeSignatureHelp(path, pos) /** * Computes call info (current parameter index, function index, functions) for a method call. @@ -91,10 +97,10 @@ object Signatures { */ def computeSignatureHelp(path: List[tpd.Tree], span: Span)(using Context): (Int, Int, List[Signature]) = findEnclosingApply(path, span) match - case Apply(fun, params) => applyCallInfo(span, params, fun) + case Apply(fun, params) => applyCallInfo(span, params, fun, false) case UnApply(fun, _, patterns) => unapplyCallInfo(span, fun, patterns) case appliedTypeTree @ AppliedTypeTree(_, types) => appliedTypeTreeCallInfo(appliedTypeTree, types) - case tp @ TypeApply(fun, types) => applyCallInfo(span, types, fun, true) + case tp @ TypeApply(fun, types) => applyCallInfo(span, types, fun, isTypeApply = true) case _ => (0, 0, Nil) @@ -136,7 +142,6 @@ object Signatures { val enclosingTree = enclosingFunction.getOrElse(expr) findEnclosingApply(Interactive.pathTo(enclosingTree, span), span) - case direct :: enclosing :: _ if isClosingSymbol(direct.source(span.end -1)) => enclosing case direct :: _ => direct @@ -153,13 +158,26 @@ object Signatures { types: List[tpd.Tree] )(using Context): (Int, Int, List[Signature]) = val typeName = fun.symbol.name.show - val typeParams = fun.symbol.typeRef.typeParams.map(_.paramName.show) + val typeParams = fun.symbol.typeRef.typeParams.map(_.paramName.show).map(TypeParam.apply(_)) val denot = fun.denot.asSingleDenotation val activeParameter = (types.length - 1) max 0 - val signature = Signature(typeName, typeParams, Nil, Some(typeName) , None, Some(denot)) + val signature = Signature(typeName, List(typeParams), Some(typeName) , None, Some(denot)) (activeParameter, 0, List(signature)) + private def findOutermostCurriedApply(untpdPath: List[untpd.Tree]): Option[untpd.GenericApply] = + val trimmedPath = untpdPath.dropWhile(!_.isInstanceOf[untpd.GenericApply]) + val maybeCurriedTrees = (trimmedPath zip trimmedPath.drop(1)).takeWhile: + case (currentTree: untpd.GenericApply, nextTree: untpd.GenericApply) => + nextTree.fun == currentTree + case _ => false + .map(_._2) + + maybeCurriedTrees.lastOption.orElse: + trimmedPath.headOption + .collect: + case genericApply: untpd.GenericApply => genericApply + /** * Extracts call information for a function application and type application. * @@ -183,9 +201,10 @@ object Signatures { case Select(qual, _) => qual case _ => tree + val paramssListIndex = findParamssIndex(fun) val (alternativeIndex, alternatives) = fun.tpe match case err: ErrorType => - val (alternativeIndex, alternatives) = alternativesFromError(err, params) match + val (alternativeIndex, alternatives) = alternativesFromError(err, params, paramssListIndex) match // if we have no alternatives from error, we have to fallback to function denotation // Check `partialyFailedCurriedFunctions` test for example case (_, Nil) => @@ -204,21 +223,63 @@ object Signatures { (alternativeIndex, alternatives) if alternativeIndex < alternatives.length then - val curriedArguments = countParams(fun, alternatives(alternativeIndex)) - // We have to shift all arguments by number of type parameters to properly show activeParameter index - val typeParamsShift = if !isTypeApply then fun.symbol.denot.paramSymss.flatten.filter(_.isType).length else 0 - val paramIndex = params.indexWhere(_.span.contains(span)) match - case -1 => (params.length - 1 max 0) + curriedArguments + typeParamsShift - case n => n + curriedArguments + typeParamsShift + val alternativeSymbol = alternatives(alternativeIndex).symbol + val safeParamssListIndex = paramssListIndex min (alternativeSymbol.paramSymss.length - 1) + val previousArgs = alternativeSymbol.paramSymss.take(safeParamssListIndex).foldLeft(0)(_ + _.length) + + val untpdPath: List[untpd.Tree] = NavigateAST + .untypedPath(fun, false).collect { case untpdTree: untpd.Tree => untpdTree } + + val untpdArgs = untpdPath match + case (Ident(_) | Select(_, _)) :: New(_) :: Select(_, name) :: untpd.Apply(untpdFun, args) :: _ + if name.isConstructorName => args + case _ :: untpd.Apply(_, args) :: _ => args + case _ :: untpd.TypeApply(_, args) :: _ => args + case _ => Nil + + val currentParamsIndex = (untpdArgs.indexWhere(_.span.contains(span)) match + case -1 if untpdArgs.isEmpty => 0 + case -1 => + commaIndex(untpdArgs, span) match + // comma is before CURSOR, so we are in parameter b example: test("a", CURSOR) + case Some(index) if index <= span.end => untpdArgs.takeWhile(_.span.end < span.start).length + // comma is after CURSOR, so we are in parameter a example: test("a" CURSOR,) + case Some(index) => untpdArgs.takeWhile(_.span.start < span.end).length - 1 + // we are either in first or last parameter + case None => + if untpdArgs.head.span.start >= span.end then 0 + else untpdArgs.length - 1 max 0 + + case n => n + ) min (alternativeSymbol.paramSymss(safeParamssListIndex).length - 1) val pre = treeQualifier(fun) val alternativesWithTypes = alternatives.map(_.asSeenFrom(pre.tpe.widenTermRefExpr)) - val alternativeSignatures = alternativesWithTypes.flatMap(toApplySignature) + val alternativeSignatures = alternativesWithTypes + .flatMap(toApplySignature(_, findOutermostCurriedApply(untpdPath), safeParamssListIndex)) - (paramIndex, alternativeIndex, alternativeSignatures) + val finalParamIndex = currentParamsIndex + previousArgs + (finalParamIndex, alternativeIndex, alternativeSignatures) else (0, 0, Nil) + /** Parser ignores chars between arguments, we have to manually find the index of comma + * @param untpdArgs List of applied untyped arguments + * @param span The position of the cursor + * + * @return None if we are in first or last parameter, comma index otherwise + */ + private def commaIndex(untpdArgs: List[untpd.Tree], span: Span)(using Context): Option[Int] = + val previousArgIndex = untpdArgs.lastIndexWhere(_.span.end < span.end) + for + previousArg <- untpdArgs.lift(previousArgIndex) + nextArg = untpdArgs.lift(previousArgIndex + 1) + text = ctx.source.content.slice(previousArg.span.end - 1, nextArg.map(_.span.start).getOrElse(span.end)) + commaIndex = text.indexOf(',') + if commaIndex != -1 + yield + commaIndex + previousArg.span.end + /** * Extracts call informatioin for function in unapply context. * @@ -235,13 +296,12 @@ object Signatures { patterns: List[tpd.Tree] )(using Context): (Int, Int, List[Signature]) = val resultType = unapplyMethodResult(fun) - val isUnapplySeq = fun.denot.name == core.Names.termName("unapplySeq") val denot = fun.denot.mapInfo(_ => resultType) val paramTypes = extractParamTypess(resultType, denot, patterns.size).flatten.map(stripAllAnnots) val paramNames = extractParamNamess(resultType, denot).flatten - val activeParameter = unapplyParameterIndex(patterns, span, paramTypes.length) + val activeParameter = patterns.takeWhile(_.span.end < span.start).length min (paramTypes.length - 1) val unapplySignature = toUnapplySignature(denot.asSingleDenotation, paramNames, paramTypes).toList (activeParameter, 0, unapplySignature) @@ -303,27 +363,22 @@ object Signatures { case other => other.stripAnnots /** - * Get index of currently edited parameter in unapply context. - * - * @param patterns Currently applied patterns for unapply method - * @param span The position of the cursor - * @param maximumParams Number of parameters taken by unapply method - * @return Index of currently edited parameter - */ - private def unapplyParameterIndex(patterns: List[tpd.Tree], span: Span, maximumParams: Int)(using Context): Int = - val patternPosition = patterns.indexWhere(_.span.contains(span)) - (patternPosition, patterns.length) match - case (-1, 0) => 0 // there are no patterns yet so it must be first one - case (-1, pos) => -1 // there are patterns, we must be outside range so we set no active parameter - case _ => (maximumParams - 1) min patternPosition max 0 // handle unapplySeq to always highlight Seq[A] on elements - - /** - * Checks if tree is valid for signatureHelp. Skipped trees are either tuple type or function type + * Checks if tree is valid for signatureHelp. Skipped trees are either tuple or function applies * * @param tree tree to validate */ private def isValid(tree: tpd.Tree)(using Context): Boolean = - !ctx.definitions.isTupleNType(tree.tpe) && !ctx.definitions.isFunctionNType(tree.tpe) + val isTupleApply = + tree.symbol.name == nme.apply + && tree.symbol.exists + && ctx.definitions.isTupleClass(tree.symbol.owner.companionClass) + + val isFunctionNApply = + tree.symbol.name == nme.apply + && tree.symbol.exists + && ctx.definitions.isFunctionSymbol(tree.symbol.owner) + + !isTupleApply && !isFunctionNApply /** * Get unapply method result type omiting unknown types and another method calls. @@ -397,85 +452,136 @@ object Signatures { * * @return Signature if denot is a function, None otherwise */ - private def toApplySignature(denot: SingleDenotation)(using Context): Option[Signature] = { + private def toApplySignature( + denot: SingleDenotation, + untpdFun: Option[untpd.GenericApply], + paramssIndex: Int + )(using Context): Option[Signature] = { val symbol = denot.symbol val docComment = ParsedComment.docOf(symbol) def isDummyImplicit(res: MethodType): Boolean = res.resultType.isParameterless && res.isImplicitMethod && - res.paramInfos.forall(info => - info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass)) - - def toParamss(tp: Type)(using Context): List[List[Param]] = - val rest = tp.resultType match - case res: MethodType => if isDummyImplicit(res) then Nil else toParamss(res) - case _ => Nil - - val currentParams = (tp.paramNamess, tp.paramInfoss) match - case (params :: _, infos :: _) => params zip infos - case _ => Nil - - val params = currentParams.map { (name, info) => - Signatures.Param( - name.show, - info.widenTermRefExpr.show, - docComment.flatMap(_.paramDoc(name)), - isImplicit = tp.isImplicitMethod - ) - } - - (params :: rest) + ( + res.paramNames.forall(name => + name.startsWith(NameKinds.ContextBoundParamName.separator) || + name.startsWith(NameKinds.ContextFunctionParamName.separator)) || + res.paramInfos.forall(info => + info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass)) + ) + + def toApplyList(tree: untpd.GenericApply): List[untpd.GenericApply] = + tree match + case untpd.GenericApply(fun: untpd.GenericApply, args) => toApplyList(fun) :+ tree + case _ => List(tree) + + def toMethodTypeList(tpe: Type): List[Type] = + tpe.resultType match + case res: MethodOrPoly => toMethodTypeList(res) :+ tpe + case res => List(tpe) def isSyntheticEvidence(name: String) = if !name.startsWith(NameKinds.ContextBoundParamName.separator) then false else symbol.paramSymss.flatten.find(_.name.show == name).exists(_.flags.is(Flags.Implicit)) - denot.info.stripPoly match - case tpe: (MethodType | AppliedType | TypeRef | TypeParamRef) => - val paramss = toParamss(tpe).map(_.filterNot(param => isSyntheticEvidence(param.name))) - val evidenceParams = (tpe.paramNamess.flatten zip tpe.paramInfoss.flatten).flatMap { - case (name, AppliedType(tpe, (ref: TypeParamRef) :: _)) if isSyntheticEvidence(name.show) => - Some(ref.paramName -> tpe) - case _ => None - } - - val typeParams = denot.info match - case poly: PolyType => - val tparams = poly.paramNames.zip(poly.paramInfos) - tparams.map { (name, info) => - evidenceParams.find((evidenceName: TypeName, _: Type) => name == evidenceName).flatMap { - case (_, tparam) => tparam.show.split('.').lastOption - } match { - case Some(evidenceTypeName) => s"${name.show}: ${evidenceTypeName}" - case None => name.show + info.show - } - } + def toTypeParam(tpe: PolyType): List[Param] = + val evidenceParams = (tpe.paramNamess.flatten zip tpe.paramInfoss.flatten).flatMap: + case (name, AppliedType(tpe, (ref: TypeParamRef) :: _)) if isSyntheticEvidence(name.show) => + Some(ref.paramName -> tpe) + case _ => None + + val tparams = tpe.paramNames.zip(tpe.paramInfos) + tparams.map: (name, info) => + evidenceParams.find((evidenceName: TypeName, _: Type) => name == evidenceName).flatMap: + case (_, tparam) => tparam.show.split('.').lastOption + match + case Some(evidenceTypeName) => TypeParam(s"${name.show}: ${evidenceTypeName}") + case None => TypeParam(name.show + info.show) + + def toParamss(tp: Type, fun: Option[untpd.GenericApply])(using Context): List[List[Param]] = + val paramSymss = symbol.paramSymss + + def reduceToParamss(applies: List[untpd.Tree], types: List[Type], paramList: Int = 0): List[List[Param]] = + applies -> types match + case ((_: untpd.TypeApply) :: restTrees, (poly: PolyType) :: restTypes) => + toTypeParam(poly) :: reduceToParamss(restTrees, restTypes, paramList + 1) + case (restTrees, (poly: PolyType) :: restTypes) => + toTypeParam(poly) :: reduceToParamss(restTrees, restTypes, paramList + 1) + case ((apply: untpd.GenericApply) :: other, tpe :: otherType) => + toParams(tpe, Some(apply), paramList) :: reduceToParamss(other, otherType, paramList + 1) + case (other, (tpe @ MethodTpe(names, _, _)) :: otherType) if !isDummyImplicit(tpe) => + toParams(tpe, None, paramList) :: reduceToParamss(other, otherType, paramList + 1) case _ => Nil - val (name, returnType) = - if (symbol.isConstructor) then - (symbol.owner.name.show, None) - else - (denot.name.show, Some(tpe.finalResultType.widenTermRefExpr.show)) - Some(Signatures.Signature(name, typeParams, paramss, returnType, docComment.map(_.mainDoc), Some(denot))) - case other => None - } - @deprecated("Deprecated in favour of `signatureHelp` which now returns Signature along SingleDenotation", "3.1.3") - def toSignature(denot: SingleDenotation)(using Context): Option[Signature] = { - if denot.name.isUnapplyName then - val resultType = denot.info.stripPoly.finalResultType match - case methodType: MethodType => methodType.resultType.widen - case other => other + def toParams(tp: Type, apply: Option[untpd.GenericApply], paramList: Int)(using Context): List[Param] = + val currentParams = (paramSymss.lift(paramList), tp.paramInfoss.headOption) match + case (Some(params), Some(infos)) => params zip infos + case _ => Nil - // We can't get already applied patterns so we won't be able to get proper signature in case when - // it can be both name-based or single match extractor. See test `nameBasedTest` - val paramTypes = extractParamTypess(resultType, denot, 0).flatten.map(stripAllAnnots) - val paramNames = extractParamNamess(resultType, denot).flatten + val params = currentParams.map: (symbol, info) => + // TODO after we migrate ShortenedTypePrinter into the compiler, it should rely on its api + val name = if symbol.isAllOf(Flags.Given | Flags.Param) && symbol.name.startsWith("x$") then nme.EMPTY else symbol.name.asTermName - toUnapplySignature(denot.asSingleDenotation, paramNames, paramTypes) - else - toApplySignature(denot) + Signatures.MethodParam( + name.show, + info.widenTermRefExpr.show, + docComment.flatMap(_.paramDoc(name)), + isImplicit = tp.isImplicitMethod, + ) + + val finalParams = apply.map: apply => + apply.args match + case Nil => params + case n if n.length > params.length => params + case _ => + // map argument order with their corresponding order so + // def foo(a: Int, b: Int, c: Int, d: Int) + // foo(b = 0, a = 0, d = 0, c = 0) order is List(1, 0, 3, 2) + // and if there are missing arguments, they are set to -1 so for the same method: + // foo(b= 0, d = 0, ) order is List(1, 3, -1, -1) + val argsWithOriginalIndices = apply.args.map: + case untpd.NamedArg(name, _) => + params.indexWhere(_.name == name.toString) + case param => -1 + .padTo(params.length, -1) + + var remainingParams = params.zipWithIndex.filter: (param, index) => + !argsWithOriginalIndices.contains(index) + .map(_._1) + + val result = argsWithOriginalIndices.map: + case -1 => + val h = remainingParams.head + remainingParams = remainingParams.tail + h + case n => params(n) + + val isReordered = params != result + val (ordered, reordered) = params.zip(result).span: (definitionMember, reorderedMember) => + definitionMember == reorderedMember + + ordered.map(_._2) ++ reordered.map(_._2.copy(isReordered = isReordered)) + + + finalParams.getOrElse(params) + end toParams + + val applies = untpdFun.map(toApplyList).getOrElse(Nil) + val types = toMethodTypeList(tp).reverse + + reduceToParamss(applies, types) + + val paramss = toParamss(denot.info, untpdFun) + val (name, returnType) = + if (symbol.isConstructor) then + (symbol.owner.name.show, None) + else + denot.symbol.defTree match + // if there is an error in denotation type, we will fallback to source tree + case defn: tpd.DefDef if denot.info.isErroneous => (denot.name.show, Some(defn.tpt.show)) + case _ => (denot.name.show, Some(denot.info.finalResultType.widenTermRefExpr.show)) + Some(Signatures.Signature(name, paramss, returnType, docComment.map(_.mainDoc), Some(denot))) } /** @@ -493,34 +599,31 @@ object Signatures { */ private def toUnapplySignature(denot: SingleDenotation, paramNames: List[Name], paramTypes: List[Type])(using Context): Option[Signature] = val params = if paramNames.length == paramTypes.length then - (paramNames zip paramTypes).map((name, info) => Param(name.show, info.show)) + (paramNames zip paramTypes).map((name, info) => MethodParam(name.show, info.show)) else - paramTypes.map(info => Param("", info.show)) + // even if we only show types of arguments, they are still method params + paramTypes.map(info => MethodParam("", info.show)) - if params.nonEmpty then - Some(Signature("", Nil, List(params), None, None, Some(denot))) - else - None + if params.nonEmpty then Some(Signature("", List(params), None, None, Some(denot))) + else None /** - * The number of parameters before `tree` application. It is necessary to properly show - * parameter number for erroneous applications before current one. - * - * This handles currying, so for an application such as `foo(1, 2)(3)`, the result of - * `countParams` should be 3. It also takes into considerations unapplied arguments so for `foo(1)(3)` - * we will still get 3, as first application `foo(1)` takes 2 parameters with currently only 1 applied. + * The number of params lists before `tree` application. + * It handles currying, so for an application such as `foo(1, 2)(3)`, the result of + * `findParamssIndex` should be 1. * * @param tree The tree to inspect. - * @param denot Denotation of function we are trying to apply * @param alreadyCurried Number of subsequent Apply trees before current tree * - * @return The number of parameters that are passed. + * @return The index of paramss we are currently in. */ - private def countParams(tree: tpd.Tree, denot: SingleDenotation, alreadyCurried: Int = 0)(using Context): Int = + private def findParamssIndex(tree: tpd.Tree, alreadyCurried: Int = 0)(using Context): Int = tree match - case Apply(fun, params) => - countParams(fun, denot, alreadyCurried + 1) + denot.symbol.paramSymss(alreadyCurried).length - case _ => 0 + case GenericApply(fun, params) + if params.nonEmpty && params.forall(_.isInstanceOf[untpd.SearchFailureIdent]) => + findParamssIndex(fun, alreadyCurried) + case GenericApply(fun, params) => findParamssIndex(fun, alreadyCurried + 1) + case _ => alreadyCurried /** * Inspect `err` to determine, if it is an error related to application of an overloaded @@ -530,35 +633,32 @@ object Signatures { * given the parameters `params`: The alternative that has the most formal parameters * matching the given arguments is chosen. * - * @param err The error message to inspect. - * @param params The parameters that were given at the call site. + * @param err The error message to inspect. + * @param params The parameters that were given at the call site. + * @param alreadyCurried Index of paramss we are currently in. * * @return A pair composed of the index of the best alternative (0 if no alternatives * were found), and the list of alternatives. */ - private def alternativesFromError(err: ErrorType, params: List[tpd.Tree])(using Context): (Int, List[SingleDenotation]) = { + private def alternativesFromError(err: ErrorType, params: List[tpd.Tree], paramssIndex: Int)(using Context): (Int, List[SingleDenotation]) = { val alternatives = err.msg match case msg: AmbiguousOverload => msg.alternatives case msg: NoMatchingOverload => msg.alternatives case _ => Nil - // If the user writes `foo(bar, )`, the typer will insert a synthetic - // `null` parameter: `foo(bar, null)`. This may influence what's the "best" - // alternative, so we discard it. - val userParams = params match - case xs :+ (nul @ Literal(Constant(null))) if nul.span.isZeroExtent => xs - case _ => params - val userParamsTypes = userParams.map(_.tpe) + val userParamsTypes = params.map(_.tpe) // Assign a score to each alternative (how many parameters are correct so far), and // use that to determine what is the current active signature. val alternativesScores = alternatives.map { alt => + val alreadyCurriedBonus = if (alt.symbol.paramSymss.length > paramssIndex) 1 else 0 alt.info.stripPoly match - case tpe: MethodType => + case tpe: MethodType => alreadyCurriedBonus + userParamsTypes.zip(tpe.paramInfos).takeWhile{ case (t0, t1) => t0 <:< t1 }.size case _ => 0 } + val bestAlternative = if (alternativesScores.isEmpty) 0 else alternativesScores.zipWithIndex.maxBy(_._1)._2 diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 903a5ae3b36a..3604e38375e7 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -552,13 +552,18 @@ class DottyLanguageServer extends LanguageServer override def signatureHelp(params: TextDocumentPositionParams) = computeAsync { canceltoken => val uri = new URI(params.getTextDocument.getUri) val driver = driverFor(uri) - implicit def ctx: Context = driver.currentCtx - val pos = sourcePosition(driver, uri, params.getPosition) - val trees = driver.openedTrees(uri) - val path = Interactive.pathTo(trees, pos) - val (paramN, callableN, signatures) = Signatures.signatureHelp(path, pos.span) - new SignatureHelp(signatures.map(signatureToSignatureInformation).asJava, callableN, paramN) + driver.compilationUnits.get(uri) match + case Some(unit) => + given newCtx: Context = driver.currentCtx.fresh.setCompilationUnit(unit) + val pos = sourcePosition(driver, uri, params.getPosition) + val adjustedSpan = pos.span.withEnd(pos.span.end + 1) + val path = Interactive.pathTo(ctx.compilationUnit.tpdTree, adjustedSpan) + val (paramN, callableN, signatures) = Signatures.signatureHelp(path, adjustedSpan) + + new SignatureHelp(signatures.map(signatureToSignatureInformation).asJava, callableN, paramN) + + case _ => new SignatureHelp() } @@ -930,23 +935,25 @@ object DottyLanguageServer { /** Convert `signature` to a `SignatureInformation` */ def signatureToSignatureInformation(signature: Signatures.Signature): lsp4j.SignatureInformation = { - val tparams = signature.tparams.map(Signatures.Param("", _)) val paramInfoss = - (tparams ::: signature.paramss.flatten).map(paramToParameterInformation) + (signature.paramss.flatten).map(paramToParameterInformation) val paramLists = - if signature.paramss.forall(_.isEmpty) && tparams.nonEmpty then - "" - else - signature.paramss - .map { paramList => - val labels = paramList.map(_.show) - val prefix = if paramList.exists(_.isImplicit) then "using " else "" - labels.mkString(prefix, ", ", "") - } - .mkString("(", ")(", ")") - val tparamsLabel = if (signature.tparams.isEmpty) "" else signature.tparams.mkString("[", ", ", "]") + signature.paramss + .map { paramList => + val labels = paramList.map(_.show) + val isImplicit = paramList.exists: + case p: Signatures.MethodParam => p.isImplicit + case _ => false + val prefix = if isImplicit then "using " else "" + val isTypeParams = paramList.forall(_.isInstanceOf[Signatures.TypeParam]) && paramList.nonEmpty + val wrap: String => String = label => if isTypeParams then + s"[$label]" + else + s"($label)" + wrap(labels.mkString(prefix, ", ", "")) + }.mkString val returnTypeLabel = signature.returnType.map(t => s": $t").getOrElse("") - val label = s"${signature.name}$tparamsLabel$paramLists$returnTypeLabel" + val label = s"${signature.name}$paramLists$returnTypeLabel" val documentation = signature.doc.map(DottyLanguageServer.markupContent) val sig = new lsp4j.SignatureInformation(label) sig.setParameters(paramInfoss.asJava) diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index 78fd99d0c3ed..433b2665c4c1 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -4,38 +4,21 @@ import org.junit.Test import dotty.tools.languageserver.util.Code._ -import dotty.tools.dotc.util.Signatures.{Param => P, Signature => S} +import dotty.tools.dotc.util.Signatures.{TypeParam => TP, MethodParam => P, Signature => S} class SignatureHelpTest { @Test def fromJava: Unit = { val signature = - S("codePointAt", Nil, List(List(P("x$0", "Int"))), Some("Int")) + S("codePointAt", List(List(P("x$0", "Int"))), Some("Int")) code"""object O { "hello".codePointAt($m1) }""" .signatureHelp(m1, List(signature), Some(0), 0) } - @Test def properFunctionReturnWithoutParenthesis: Unit = { - val listSignature = S("apply", List("A"), List(List(P("elems", "A*"))), Some("List[A]")) - val optionSignature = S("apply", List("A"), List(List(P("x", "A"))), Some("Option[A]")) - code"""object O { - | List(1, 2$m1 - |} - |object T { - | List(Option(1$m2 - |} - |object Z { - | List(Option(1)$m3 - |}""" - .signatureHelp(m1, List(listSignature), Some(0), 1) - .signatureHelp(m2, List(optionSignature), Some(0), 1) - .signatureHelp(m3, List(listSignature), Some(0), 1) - } - @Test def errorTypeParameter: Unit = { - val emptySignature = S("empty", List("K", "V"), Nil, Some("Map[K, V]")) + val emptySignature = S("empty", List(List(TP("K"), TP("V"))), Some("Map[K, V]")) code"""object O: | Map.empty[WrongType, $m1] """ @@ -43,8 +26,8 @@ class SignatureHelpTest { } @Test def methodTypeParameter: Unit = { - val applySignature = S("apply", List("K", "V"), List(List(P("elems", "(K, V)*"))), Some("Map[K, V]")) - val emptySignature = S("empty", List("K", "V"), Nil, Some("Map[K, V]")) + val applySignature = S("apply", List(List(TP("K"), TP("V")), List(P("elems", "(K, V)*"))), Some("Map[K, V]")) + val emptySignature = S("empty", List(List(TP("K"), TP("V"))), Some("Map[K, V]")) code"""object O: | Map[$m1] | Map.empty[$m2] @@ -56,7 +39,7 @@ class SignatureHelpTest { } @Test def classTypeParameter: Unit = { - val signature = S("Test", List("K", "V"), Nil, Some("Test")) + val signature = S("Test", List(List(TP("K"), TP("V"))), Some("Test")) code"""object O: | class Test[K, V] {} | new Test[$m1] @@ -67,7 +50,7 @@ class SignatureHelpTest { } @Test def traitTypeParameter: Unit = { - val signature = S("Test", List("K", "V"), Nil, Some("Test")) + val signature = S("Test", List(List(TP("K"), TP("V"))), Some("Test")) code"""object O: | trait Test[K, V] {} | new Test[$m1] {} @@ -78,7 +61,7 @@ class SignatureHelpTest { } @Test def typeAliasTypeParameter: Unit = { - val signature = S("Test", List("K"), Nil, Some("Test")) + val signature = S("Test", List(List(TP("K"))), Some("Test")) code"""object O: | type Test[K] = List[K] | def test(x: Test[$m1]) @@ -87,7 +70,7 @@ class SignatureHelpTest { } @Test def typeParameterIndex: Unit = { - val mapSignature = S("map", List("B"), List(List(P("f", "Int => B"))), Some("List[B]")) + val mapSignature = S("map", List(List(TP("B")), List(P("f", "Int => B"))), Some("List[B]")) code"""object O { List(1, 2, 3).map[$m1]($m2) }""" @@ -96,18 +79,18 @@ class SignatureHelpTest { } @Test def partialyFailedCurriedFunctions: Unit = { - val listSignature = S("curry", Nil, List(List(P("a", "Int"), P("b", "Int")), List(P("c", "Int"))), Some("Int")) + val listSignature = S("curry", List(List(P("a", "Int"), P("b", "Int")), List(P("c", "Int"))), Some("Int")) code"""object O { |def curry(a: Int, b: Int)(c: Int) = a | curry(1$m1)$m2(3$m3) |}""" .signatureHelp(m1, List(listSignature), Some(0), 0) - .signatureHelp(m2, List(listSignature), Some(0), 0) + .signatureHelp(m2, List(listSignature), Some(0), 2) .signatureHelp(m3, List(listSignature), Some(0), 2) } @Test def optionProperSignature: Unit = { - val signature = S("apply", List("A"), List(List(P("x", "A"))), Some("Option[A]")) + val signature = S("apply", List(List(TP("A")), List(P("x", "A"))), Some("Option[A]")) code"""object O { | Option(1, 2, 3, $m1) |}""" @@ -123,8 +106,8 @@ class SignatureHelpTest { } @Test def fromScala2: Unit = { - val applySig = S("apply", List("A"), List(List(P("elems", "A*"))), Some("List[A]")) - val mapSig = S("map", List("B"), List(List(P("f", "Int => B"))), Some("List[B]")) + val applySig = S("apply", List(List(TP("A")), List(P("elems", "A*"))), Some("List[A]")) + val mapSig = S("map", List(List(TP("B")), List(P("f", "Int => B"))), Some("List[B]")) code"""object O { List($m1) List(1, 2, 3).map($m2) @@ -134,14 +117,14 @@ class SignatureHelpTest { } @Test def typeParameterMethodApply: Unit = { - val testSig = S("method", Nil, List(List()), Some("Int")) + val testSig = S("method", List(List()), Some("Int")) code"""case class Foo[A](test: A) { | def method(): A = ??? |} |object O { | Foo(5).method($m1) |}""" - .signatureHelp(m1, List(testSig), Some(0), 0) + .signatureHelp(m1, List(testSig), Some(0), -1) } @Test def unapplyBooleanReturn: Unit = { @@ -153,11 +136,11 @@ class SignatureHelpTest { | case s @ Even(${m1}) => println(s"s has an even number of characters") | case s => println(s"s has an odd number of characters") """ - .signatureHelp(m1, Nil, Some(0), 0) + .signatureHelp(m1, Nil, Some(0), -1) } @Test def unapplyCustomClass: Unit = { - val signature = S("", Nil, List(List(P("", "Int"))), None) + val signature = S("", List(List(P("", "Int"))), None) code"""class Nat(val x: Int): | def get: Int = x @@ -174,7 +157,7 @@ class SignatureHelpTest { } @Test def unapplyTypeClass: Unit = { - val signature = S("", Nil, List(List(P("", "Int"), P("", "String"))), None) + val signature = S("", List(List(P("", "Int"), P("", "String"))), None) code"""class Two[A, B](a: A, b: B) |object Two { @@ -192,9 +175,9 @@ class SignatureHelpTest { } @Test def nestedUnapplySignature: Unit = { - val signatureOneTwo = S("", Nil, List(List(P("a", "One"), P("b", "Two"))), None) - val signatureOne = S("", Nil, List(List(P("c", "Int"))), None) - val signatureTwo = S("", Nil, List(List(P("d", "Int"))), None) + val signatureOneTwo = S("", List(List(P("a", "One"), P("b", "Two"))), None) + val signatureOne = S("", List(List(P("c", "Int"))), None) + val signatureTwo = S("", List(List(P("d", "Int"))), None) code"""case class One(c: Int) |case class Two(d: Int) @@ -216,7 +199,7 @@ class SignatureHelpTest { } @Test def properParameterIndexTest: Unit = { - val signature = S("", Nil, List(List(P("a", "Int"), P("b", "String"))), None) + val signature = S("", List(List(P("a", "Int"), P("b", "String"))), None) code"""case class Two(a: Int, b: String) | |object Main { @@ -226,13 +209,13 @@ class SignatureHelpTest { | } |}""" .signatureHelp(m1, List(signature), Some(0), 0) - .signatureHelp(m2, List(signature), Some(0), -1) - .signatureHelp(m3, List(signature), Some(0), -1) - .signatureHelp(m4, List(signature), Some(0), -1) + .signatureHelp(m2, List(signature), Some(0), 1) + .signatureHelp(m3, List(signature), Some(0), 1) + .signatureHelp(m4, List(), Some(0), 0) } @Test def unapplyClass: Unit = { - val signature = S("", Nil, List(List(P("", "Int"), P("", "String"))), None) + val signature = S("", List(List(P("", "Int"), P("", "String"))), None) code"""class Two(a: Int, b: String) |object Two { @@ -250,7 +233,7 @@ class SignatureHelpTest { } @Test def productMatch: Unit = { - val signature = S("", Nil, List(List(P("", "Char"), P("", "Char"))), None) + val signature = S("", List(List(P("", "Char"), P("", "Char"))), None) code"""class FirstChars(s: String) extends Product: | def _1 = s.charAt(0) @@ -268,7 +251,7 @@ class SignatureHelpTest { } @Test def noUnapplySignatureWhenApplyingUnapply: Unit = { - val signature = S("unapply", List("A"), List(List(P("a", "A"))), Some("Some[(A, A)]")) + val signature = S("unapply", List(List(TP("A")), List(P("a", "A"))), Some("Some[(A, A)]")) code""" |object And { @@ -282,7 +265,7 @@ class SignatureHelpTest { } @Test def nestedOptionReturnedInUnapply: Unit = { - val signature = S("", Nil, List(List(P("", "Option[Int]"))), None) + val signature = S("", List(List(P("", "Option[Int]"))), None) code"""object OpenBrowserCommand { | def unapply(command: String): Option[Option[Int]] = { @@ -298,8 +281,8 @@ class SignatureHelpTest { } @Test def unknownTypeUnapply: Unit = { - val signature = S("", Nil, List(List(P("a", "A"), P("b", "B"))), None) - val signature2 = S("", Nil, List(List(P("a", "Int"), P("b", "Any"))), None) + val signature = S("", List(List(P("a", "A"), P("b", "B"))), None) + val signature2 = S("", List(List(P("a", "Int"), P("b", "Any"))), None) code"""case class Two[A, B](a: A, b: B) |object Main { @@ -318,24 +301,22 @@ class SignatureHelpTest { } @Test def sequenceMatchUnapply: Unit = { - val signatureSeq = S("", Nil, List(List(P("", "Seq[Int]"))), None) - val signatureVariadicExtractor = S("", Nil, List(List(P("", "Int"), P("","List[Int]"))), None) + val signatureSeq = S("", List(List(P("", "Seq[Int]"))), None) + val signatureVariadicExtractor = S("", List(List(P("", "Int"), P("","List[Int]"))), None) code"""case class Two[A, B](a: A, b: B) |object Main { | Seq(1,2,3) match { | case Seq($m1) => - | case h$m2 :: t$m3 => + | case h :: t => | } |} """ .signatureHelp(m1, List(signatureSeq), Some(0), 0) - .signatureHelp(m2, List(signatureVariadicExtractor), Some(0), 0) - .signatureHelp(m3, List(signatureVariadicExtractor), Some(0), 1) } @Test def productTypeClassMatch: Unit = { - val signature = S("", Nil, List(List(P("", "String"), P("", "String"))), None) + val signature = S("", List(List(P("", "String"), P("", "String"))), None) code"""class FirstChars[A](s: A) extends Product: | def _1 = s @@ -353,8 +334,8 @@ class SignatureHelpTest { } @Test def nameBasedMatch: Unit = { - val nameBasedMatch = S("", Nil, List(List(P("", "Int"), P("", "String"))), None) - val singleMatch = S("", Nil, List(List(P("", "ProdEmpty.type"))), None) + val nameBasedMatch = S("", List(List(P("", "Int"), P("", "String"))), None) + val singleMatch = S("", List(List(P("", "ProdEmpty.type"))), None) code"""object ProdEmpty: | def _1: Int = ??? @@ -375,8 +356,8 @@ class SignatureHelpTest { } @Test def nameBasedMatchWithWrongGet: Unit = { - val nameBasedMatch = S("", Nil, List(List(P("", "Int"))), None) - val singleMatch = S("", Nil, List(List(P("", "Int"))), None) + val nameBasedMatch = S("", List(List(P("", "Int"))), None) + val singleMatch = S("", List(List(P("", "Int"))), None) code"""object ProdEmpty: | def _1: Int = ??? @@ -392,12 +373,12 @@ class SignatureHelpTest { | case _ => () """ .signatureHelp(m1, List(nameBasedMatch), Some(0), 0) - .signatureHelp(m2, List(nameBasedMatch), Some(0), -1) + .signatureHelp(m2, List(nameBasedMatch), Some(0), 0) .signatureHelp(m3, List(singleMatch), Some(0), 0) } @Test def nameBasedSingleMatchOrder: Unit = { - val signature = S("", Nil, List(List(P("", "String"))), None) + val signature = S("", List(List(P("", "String"))), None) code"""object ProdEmpty: | def _1: Int = 1 @@ -414,7 +395,7 @@ class SignatureHelpTest { } @Test def getObjectMatch: Unit = { - val signature = S("", Nil, List(List(P("", "String"))), None) + val signature = S("", List(List(P("", "String"))), None) code"""object ProdEmpty: | def isEmpty = true @@ -430,7 +411,7 @@ class SignatureHelpTest { } @Test def customSequenceMatch: Unit = { - val signature = S("", Nil, List(List(P("", "Seq[Char]"))), None) + val signature = S("", List(List(P("", "Seq[Char]"))), None) code"""object CharList: | def unapplySeq(s: String): Option[Seq[Char]] = Some(s.toList) @@ -448,7 +429,7 @@ class SignatureHelpTest { } @Test def productSequenceMatch: Unit = { - val signature = S("", Nil, List(List(P("", "String"), P("", "Seq[Int]"))), None) + val signature = S("", List(List(P("", "String"), P("", "Seq[Int]"))), None) code"""class Foo(val name: String, val children: Int*) |object Foo: @@ -468,7 +449,7 @@ class SignatureHelpTest { } @Test def productSequenceMatchForCaseClass: Unit = { - val signature = S("", Nil, List(List(P("name", "String"), P("children", "Seq[Int]"))), None) + val signature = S("", List(List(P("name", "String"), P("children", "Seq[Int]"))), None) code"""case class Foo(val name: String, val children: Int*) | @@ -485,7 +466,7 @@ class SignatureHelpTest { } @Test def unapplyManyType: Unit = { - val signature = S("", Nil, List(List(P("", "Int"), P("", "String"))), None) + val signature = S("", List(List(P("", "Int"), P("", "String"))), None) code""" |object Opt { @@ -502,7 +483,7 @@ class SignatureHelpTest { } @Test def unapplyTypeCaseClass: Unit = { - val signature = S("", Nil, List(List(P("a", "Int"), P("b", "String"))), None) + val signature = S("", List(List(P("a", "Int"), P("b", "String"))), None) code"""case class Two[A, B](a: A, b: B) | @@ -517,7 +498,7 @@ class SignatureHelpTest { } @Test def unapplyForTuple: Unit = { - val signature = S("", Nil, List(List(P("", "Int"), P("", "Int"))), None) + val signature = S("", List(List(P("", "Int"), P("", "Int"))), None) code"""object Main { | (1, 2) match | case (x${m1}, ${m2}) => @@ -527,7 +508,7 @@ class SignatureHelpTest { } @Test def unapplyCaseClass: Unit = { - val signature = S("", Nil, List(List(P("a", "Int"), P("b", "String"))), None) + val signature = S("", List(List(P("a", "Int"), P("b", "String"))), None) code"""case class Two(a: Int, b: String) | @@ -542,7 +523,7 @@ class SignatureHelpTest { } @Test def unapplyOption: Unit = { - val signature = S("", Nil, List(List(P("", "Int"))), None) + val signature = S("", List(List(P("", "Int"))), None) code"""|object Main { | Option(1) match { @@ -553,7 +534,7 @@ class SignatureHelpTest { } @Test def unapplyWithImplicits: Unit = { - val signature = S("", Nil, List(List(P("", "Int"))), None) + val signature = S("", List(List(P("", "Int"))), None) code"""| |object Opt: | def unapply[A](using String)(a: Option[A])(using Int) = a @@ -570,7 +551,7 @@ class SignatureHelpTest { } @Test def unapplyWithMultipleImplicits: Unit = { - val signature = S("", Nil, List(List(P("", "Int"))), None) + val signature = S("", List(List(P("", "Int"))), None) code"""| |object Opt: | def unapply[A](using String)(using Int)(a: Option[A]) = a @@ -587,15 +568,10 @@ class SignatureHelpTest { /** 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")), + val foo1Sig = S("foo1", List(List(P("param0", "Int"))), Some("Int")) + val foo2Sig = S("foo2", List(List(P("param0", "Int"))), Some("Int")) + val foo3Sig = S("foo3", List(List(P("param0", "Int")), List(P("dummy", "DummyImplicit"))), Some("Int")) + val foo4Sig = S("foo4", 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 = ??? @@ -615,7 +591,7 @@ class SignatureHelpTest { @Test def singleParam: Unit = { val signature = - S("foo", Nil, List(List(P("param0", "Int"))), Some("Int")) + S("foo", List(List(P("param0", "Int"))), Some("Int")) code"""object O { def foo(param0: Int): Int = ??? foo($m1) @@ -626,7 +602,7 @@ class SignatureHelpTest { } @Test def twoParams: Unit = { - val signature = S("foo", Nil, List(List(P("param0", "Int"), P("param1", "String"))), Some("Int")) + val signature = S("foo", List(List(P("param0", "Int"), P("param1", "String"))), Some("Int")) code"""object O { def foo(param0: Int, param1: String): Int = ??? foo($m1) @@ -639,8 +615,8 @@ class SignatureHelpTest { } @Test def noMatchingOverload: Unit = { - val sig0 = S("foo", Nil, List(List(P("param0", "Int"))), Some("Nothing")) - val sig1 = S("foo", Nil, List(List(P("param1", "String"))), Some("Nothing")) + val sig0 = S("foo", List(List(P("param0", "Int"))), Some("Nothing")) + val sig1 = S("foo", List(List(P("param1", "String"))), Some("Nothing")) code"""object O { def foo(param0: Int): Nothing = ??? @@ -655,8 +631,8 @@ class SignatureHelpTest { } @Test def singleMatchingOverload: Unit = { - val sig0 = S("foo", Nil, List(List(P("param0", "Int"), P("param1", "String"))), Some("Nothing")) - val sig1 = S("foo", Nil, List(List(P("param0", "String"), P("param1", "Int"))), Some("Nothing")) + val sig0 = S("foo", List(List(P("param0", "Int"), P("param1", "String"))), Some("Nothing")) + val sig1 = S("foo", List(List(P("param0", "String"), P("param1", "Int"))), Some("Nothing")) code"""object O { def foo(param0: Int, param1: String): Nothing = ??? def foo(param0: String, param1: Int): Nothing = ??? @@ -674,9 +650,9 @@ class SignatureHelpTest { } @Test def multipleMatchingOverloads: Unit = { - val sig0 = S("foo", Nil, List(List(P("param0", "Int"), P("param1", "Int"))), Some("Nothing")) - val sig1 = S("foo", Nil, List(List(P("param0", "Int"), P("param1", "Boolean"))), Some("Nothing")) - val sig2 = S("foo", Nil, List(List(P("param0", "String"), P("param1", "Int"))), Some("Nothing")) + val sig0 = S("foo", List(List(P("param0", "Int"), P("param1", "Int"))), Some("Nothing")) + val sig1 = S("foo", List(List(P("param0", "Int"), P("param1", "Boolean"))), Some("Nothing")) + val sig2 = S("foo", List(List(P("param0", "String"), P("param1", "Int"))), Some("Nothing")) val sigs = List(sig0, sig1, sig2) code"""object O { def foo(param0: Int, param1: Int): Nothing = ??? @@ -702,8 +678,8 @@ class SignatureHelpTest { } @Test def ambiguousOverload: Unit = { - val sig0 = S("foo", Nil, List(List(P("param0", "String")), List(P("param1", "String"))), Some("Nothing")) - val sig1 = S("foo", Nil, List(List(P("param0", "String"))), Some("Nothing")) + val sig0 = S("foo", List(List(P("param0", "String")), List(P("param1", "String"))), Some("Nothing")) + val sig1 = S("foo", List(List(P("param0", "String"))), Some("Nothing")) code"""object O { def foo(param0: String)(param1: String): Nothing = ??? def foo(param0: String): Nothing = ??? @@ -713,13 +689,12 @@ class SignatureHelpTest { }""" .signatureHelp(m1, List(sig0, sig1), None, 0) .signatureHelp(m2, List(sig0, sig1), None, 0) - .signatureHelp(m3, List(sig0, sig1), Some(1), 1) + .signatureHelp(m3, List(sig0, sig1), Some(0), 1) } @Test def multipleParameterLists: Unit = { val signature = S("foo", - Nil, List( List(P("param0", "Int"), P("param1", "Int")), List(P("param2", "Int")), @@ -744,7 +719,6 @@ class SignatureHelpTest { @Test def implicitParams: Unit = { val signature = S("foo", - Nil, List( List(P("param0", "Int"), P("param1", "Int")), List(P("param2", "Int", isImplicit = true)) @@ -764,8 +738,8 @@ class SignatureHelpTest { @Test def typeParameters: Unit = { val signature = S("foo", - List("M[X]", "T[Z] <: M[Z]", "U >: T"), List( + List(TP("M[X]"), TP("T[Z] <: M[Z]"), TP("U >: T")), List(P("p0", "M[Int]"), P("p1", "T[Int]"), P("p2", "U")) ), Some("Int")) @@ -779,7 +753,6 @@ class SignatureHelpTest { @Test def constructorCall: Unit = { val signature = S("Foo", - Nil, List( List(P("x", "Int"), P("y", "String")), List(P("z", "String")) @@ -799,7 +772,6 @@ class SignatureHelpTest { @Test def overloadedConstructorCall: Unit = { val sig0 = S("Foo", - Nil, List( List(P("x", "Int"), P("y", "String")), List(P("z", "Int")) @@ -807,7 +779,6 @@ class SignatureHelpTest { None) val sig1 = S("Foo", - Nil, List( List(P("x", "Int"), P("y", "Int")) ), @@ -829,8 +800,8 @@ class SignatureHelpTest { @Test def constructorCallDoc: Unit = { val signatures = List( - S("Foo", Nil, List(List(P("x", "Int", Some("An int")), P("y", "String", Some("A string")))), None, Some("A Foo")), - S("Foo", Nil, List(List(P("z", "Boolean", Some("A boolean")), P("foo", "Foo", Some("A Foo")))), None, Some("An alternative constructor for Foo")) + S("Foo", List(List(P("x", "Int", Some("An int")), P("y", "String", Some("A string")))), None, Some("A Foo")), + S("Foo", List(List(P("z", "Boolean", Some("A boolean")), P("foo", "Foo", Some("A Foo")))), None, Some("An alternative constructor for Foo")) ) code"""/** @@ -869,8 +840,8 @@ class SignatureHelpTest { @Test def classTypeParameters: Unit = { val signature = S("Foo", - List("M[X]", "T[Z] <: M[Z]", "U"), List( + List(TP("M[X]"), TP("T[Z] <: M[Z]"), TP("U")), List(P("p0", "M[Int]"), P("p1", "T[Int]"), P("p2", "U")), List(P("p3", "Int")) ), @@ -893,7 +864,7 @@ class SignatureHelpTest { /** Hello, world! */ def foo(param0: Int): Int = 0 foo($m1) }""" - .signatureHelp(m1, List(S("foo", Nil, List(List(P("param0", "Int"))), Some("Int"), Some("Hello, world!"))), None, 0) + .signatureHelp(m1, List(S("foo", List(List(P("param0", "Int"))), Some("Int"), Some("Hello, world!"))), None, 0) } @Test def showParamDoc: Unit = { @@ -909,7 +880,7 @@ class SignatureHelpTest { | buzz($m1) |}""" .signatureHelp(m1, List( - S("buzz", Nil, List(List( + S("buzz", List(List( P("fizz", "Int", Some("The fizz to buzz")), P("bar", "Int", Some("Buzzing limit")) )), Some("Int"), Some("Buzzes a fizz up to bar")) @@ -918,9 +889,9 @@ class SignatureHelpTest { @Test def nestedApplySignatures: Unit = { val signatures = (1 to 5).map { i => - S(s"foo$i", Nil, List(List(P("x", "Int"))), Some("Int")) + S(s"foo$i", List(List(P("x", "Int"))), Some("Int")) } - val booSignature = S(s"boo", Nil, List(List(P("x", "Int"), P("y", "Int"))), Some("Int")) + val booSignature = S(s"boo", List(List(P("x", "Int"), P("y", "Int"))), Some("Int")) code"""|object O: | def foo1(x: Int): Int = ??? | def foo2(x: Int): Int = ??? @@ -939,8 +910,8 @@ class SignatureHelpTest { } @Test def multipleNestedApplySignatures: Unit = { - val simpleSignature = S(s"simpleFoo", Nil, List(List(P("x", "Int"))), Some("Int")) - val complicatedSignature = S(s"complicatedFoo", Nil, List(List(P("x", "Int"), P("y", "Int"), P("z", "Int"))), Some("Int")) + val simpleSignature = S(s"simpleFoo", List(List(P("x", "Int"))), Some("Int")) + val complicatedSignature = S(s"complicatedFoo", List(List(P("x", "Int"), P("y", "Int"), P("z", "Int"))), Some("Int")) code"""|object O: | def simpleFoo(x: Int): Int = ??? | def complicatedFoo(x: Int, y: Int, z: Int): Int = ??? @@ -966,7 +937,7 @@ class SignatureHelpTest { } @Test def noHelpSignatureWithPositionedOnName: Unit = { - val signature = S(s"foo", Nil, List(List(P("x", "Int"))), Some("Int")) + val signature = S(s"foo", List(List(P("x", "Int"))), Some("Int")) code"""|object O: | def foo(x: Int): Int = ??? | f${m1}oo(${m2})""" @@ -974,4 +945,13 @@ class SignatureHelpTest { .signatureHelp(m2, List(signature), None, 0) } + @Test def instantiatedTypeVarInOldExtensionMethods: Unit = { + val signature = S(s"test", List(List(P("x", "Int"))), Some("List[Int]")) + code"""|object O: + | implicit class TypeVarTest[T](xs: List[T]): + | def test(x: T): List[T] = ??? + | List(1,2,3).test(${m1})""" + .signatureHelp(m1, List(signature), None, 0) + } + } diff --git a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala index bfaa56138547..f7797efbfb27 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala @@ -1,134 +1,149 @@ package dotty.tools.pc -import scala.jdk.CollectionConverters._ -import scala.meta.pc.OffsetParams -import scala.meta.pc.SymbolDocumentation -import scala.meta.pc.SymbolSearch - -import dotty.tools.dotc.ast.Trees.AppliedTypeTree -import dotty.tools.dotc.ast.Trees.TypeApply -import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.parsing.Tokens.closingRegionTokens +import dotty.tools.dotc.reporting.ErrorMessageID +import dotty.tools.dotc.reporting.ExpectedTokenButFound import dotty.tools.dotc.util.Signatures -import dotty.tools.dotc.util.Signatures.Signature import dotty.tools.dotc.util.SourceFile -import dotty.tools.dotc.util.SourcePosition +import dotty.tools.dotc.util.Spans +import dotty.tools.dotc.util.Spans.Span +import dotty.tools.pc.printer.ShortenedTypePrinter +import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam import dotty.tools.pc.utils.MtagsEnrichments.* - import org.eclipse.lsp4j as l +import scala.jdk.CollectionConverters.* +import scala.jdk.OptionConverters.* +import scala.meta.internal.metals.ReportContext +import scala.meta.pc.OffsetParams +import scala.meta.pc.SymbolDocumentation +import scala.meta.pc.SymbolSearch + object SignatureHelpProvider: def signatureHelp( driver: InteractiveDriver, params: OffsetParams, search: SymbolSearch - ) = - val uri = params.uri() - val sourceFile = SourceFile.virtual(params.uri().nn, params.text().nn) + )(using ReportContext): l.SignatureHelp = + val uri = params.uri().nn + val text = params.text().nn + val sourceFile = SourceFile.virtual(uri, text) driver.run(uri.nn, sourceFile) - given ctx: Context = driver.currentCtx - - val pos = driver.sourcePosition(params) - val trees = driver.openedTrees(uri.nn) - - val path = Interactive.pathTo(trees, pos) - - val (paramN, callableN, alternatives) = - Signatures.signatureHelp(path, pos.span) - val infos = alternatives.flatMap { signature => - signature.denot.map { - (signature, _) - } - } - - val signatureInfos = infos.map { case (signature, denot) => - search.symbolDocumentation(denot.symbol) match - case Some(doc) => - withDocumentation( - doc, - signature, - denot.symbol.is(Flags.JavaDefined) - ).getOrElse(signature) - case _ => signature - - } - - /* Versions prior to 3.2.1 did not support type parameters - * so we need to skip them. - */ - new l.SignatureHelp( - signatureInfos.map(signatureToSignatureInformation).asJava, - callableN, - paramN - ) + driver.compilationUnits.get(uri) match + case Some(unit) => + + val pos = driver.sourcePosition(params, isZeroExtent = false) + val path = Interactive.pathTo(unit.tpdTree, pos.span)(using driver.currentCtx) + + val localizedContext = Interactive.contextOfPath(path)(using driver.currentCtx) + val indexedContext = IndexedContext(driver.currentCtx) + + given Context = localizedContext.fresh + .setCompilationUnit(unit) + .setPrinterFn(_ => ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedContext)) + + val (paramN, callableN, alternatives) = Signatures.signatureHelp(path, pos.span) + + val infos = alternatives.flatMap: signature => + signature.denot.map(signature -> _) + + val signatureInfos = infos.map { case (signature, denot) => + search.symbolDocumentation(denot.symbol) match + case Some(doc) => + withDocumentation( + doc, + signature, + denot.symbol.is(Flags.JavaDefined) + ).getOrElse(signature) + case _ => signature + + } + + new l.SignatureHelp( + signatureInfos.map(signatureToSignatureInformation).asJava, + callableN, + paramN + ) + case _ => new l.SignatureHelp() end signatureHelp private def withDocumentation( info: SymbolDocumentation, signature: Signatures.Signature, isJavaSymbol: Boolean - ): Option[Signature] = - val allParams = info.parameters().nn.asScala - def updateParams( - params: List[Signatures.Param], - index: Int - ): List[Signatures.Param] = + ): Option[Signatures.Signature] = + val methodParams = info.parameters().nn.asScala + val typeParams = info.typeParameters().nn.asScala + + def updateParams(params: List[Signatures.Param], typeParamIndex: Int, methodParamIndex: Int): List[Signatures.Param] = params match - case Nil => Nil - case head :: tail => - val rest = updateParams(tail, index + 1) - allParams.lift(index) match + case (head: Signatures.MethodParam) :: tail => + val rest = updateParams(tail, typeParamIndex, methodParamIndex + 1) + methodParams.lift(methodParamIndex) match case Some(paramDoc) => val newName = if isJavaSymbol && head.name.startsWith("x$") then paramDoc.nn.displayName() else head.name - head.copy( - doc = Some(paramDoc.docstring.nn), - name = newName.nn - ) :: rest + head.copy(name = newName.nn, doc = Some(paramDoc.docstring.nn)) :: rest + case _ => head :: rest + case (head: Signatures.TypeParam) :: tail => + val rest = updateParams(tail, typeParamIndex + 1, methodParamIndex) + typeParams.lift(typeParamIndex) match + case Some(paramDoc) => + head.copy(doc = Some(paramDoc.docstring.nn)) :: rest case _ => head :: rest + case _ => Nil def updateParamss( params: List[List[Signatures.Param]], - index: Int + typeParamIndex: Int, + methodParamIndex: Int ): List[List[Signatures.Param]] = params match case Nil => Nil case head :: tail => - val updated = updateParams(head, index) - updated :: updateParamss(tail, index + head.size) - val updatedParams = updateParamss(signature.paramss, 0) + val updated = updateParams(head, typeParamIndex, methodParamIndex) + val (nextTypeParamIndex, nextMethodParamIndex) = head match + case (_: Signatures.MethodParam) :: _ => (typeParamIndex, methodParamIndex + head.size) + case (_: Signatures.TypeParam) :: _ => (typeParamIndex + head.size, methodParamIndex) + case _ => (typeParamIndex, methodParamIndex) + updated :: updateParamss(tail, nextTypeParamIndex, nextMethodParamIndex) + val updatedParams = updateParamss(signature.paramss, 0, 0) Some(signature.copy(doc = Some(info.docstring().nn), paramss = updatedParams)) end withDocumentation private def signatureToSignatureInformation( signature: Signatures.Signature ): l.SignatureInformation = - val tparams = signature.tparams.map(Signatures.Param("", _)) - val paramInfoss = - (tparams ::: signature.paramss.flatten).map(paramToParameterInformation) + val paramInfoss = (signature.paramss.flatten).map(paramToParameterInformation) val paramLists = - if signature.paramss.forall(_.isEmpty) && tparams.nonEmpty then "" - else - signature.paramss - .map { paramList => - val labels = paramList.map(_.show) - val prefix = if paramList.exists(_.isImplicit) then "using " else "" - labels.mkString(prefix, ", ", "") - } - .mkString("(", ")(", ")") - val tparamsLabel = - if signature.tparams.isEmpty then "" - else signature.tparams.mkString("[", ", ", "]") + signature.paramss + .map { paramList => + val labels = paramList.map(_.show) + val isImplicit = paramList.exists: + case p: Signatures.MethodParam => p.isImplicit + case _ => false + val prefix = if isImplicit then "using " else "" + val isTypeParams = paramList.forall(_.isInstanceOf[Signatures.TypeParam]) && paramList.nonEmpty + val wrap: String => String = label => if isTypeParams then + s"[$label]" + else + s"($label)" + wrap(labels.mkString(prefix, ", ", "")) + }.mkString + + val returnTypeLabel = signature.returnType.map(t => s": $t").getOrElse("") - val label = s"${signature.name}$tparamsLabel$paramLists$returnTypeLabel" + val label = s"${signature.name}$paramLists$returnTypeLabel" val documentation = signature.doc.map(markupContent) val sig = new l.SignatureInformation(label) sig.setParameters(paramInfoss.asJava) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index 1e2dbb5cdaa1..2f2f2bcfdc41 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -12,7 +12,6 @@ import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering} import scala.meta.pc.* import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.core.Comments.Comment import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.* @@ -27,8 +26,6 @@ import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.interactive.Completion import dotty.tools.dotc.interactive.Completion.Mode import dotty.tools.dotc.util.SourcePosition -import dotty.tools.dotc.util.Spans -import dotty.tools.dotc.util.Spans.Span import dotty.tools.dotc.util.SrcPos import dotty.tools.pc.AutoImports.AutoImportsGenerator import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala index a052a0a2c8eb..e912bc49032f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala @@ -12,7 +12,6 @@ import dotty.tools.dotc.core.Comments import dotty.tools.dotc.core.Comments.Comment import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.util.SourcePosition -import dotty.tools.dotc.util.Spans.Span object KeywordsCompletions: diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala index e4ae4070edb9..5e375f2e9821 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala @@ -9,7 +9,6 @@ import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.ContextOps.localContext import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Flags.Method import dotty.tools.dotc.core.NameKinds.DefaultGetterName diff --git a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala index 9c255d20d212..1ab58f74272e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala @@ -492,7 +492,7 @@ class ShortenedTypePrinter( case head :: Nil => s": $head" case many => many.mkString(": ", ": ", "") s"$keywordName$paramTypeString$bounds" - else if param.is(Flags.Given) && param.name.toString.contains('$') then + else if param.isAllOf(Given | Param) && param.name.startsWith("x$") then // For Anonymous Context Parameters // print only type string // e.g. "using Ord[T]" instead of "using x$0: Ord[T]" diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala index c006dda2c652..d59a7b24eee6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala @@ -36,7 +36,8 @@ object MtagsEnrichments extends CommonMtagsEnrichments: extension (driver: InteractiveDriver) def sourcePosition( - params: OffsetParams + params: OffsetParams, + isZeroExtent: Boolean = true ): SourcePosition = val uri = params.uri() val source = driver.openedFiles(uri.nn) @@ -50,6 +51,7 @@ object MtagsEnrichments extends CommonMtagsEnrichments: case offset => Spans.Span(p.offset(), p.offset()) } + case _ if !isZeroExtent => Spans.Span(params.offset(), params.offset() + 1) case _ => Spans.Span(params.offset()) new SourcePosition(source, span) @@ -273,7 +275,7 @@ object MtagsEnrichments extends CommonMtagsEnrichments: val denot = sym.denot.asSeenFrom(pre.tpe.widenTermRefExpr) (denot.info, sym.withUpdatedTpe(denot.info)) catch case NonFatal(e) => (sym.info, sym) - + def isInfix(using ctx: Context) = tree match case Select(New(_), _) => false diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseSignatureHelpSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseSignatureHelpSuite.scala index 0022be40a630..ca647502fabf 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseSignatureHelpSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseSignatureHelpSuite.scala @@ -1,10 +1,11 @@ package dotty.tools.pc.base -import java.nio.file.Paths +import org.eclipse.lsp4j.SignatureHelp +import java.nio.file.Paths import scala.jdk.CollectionConverters.* -import scala.meta.internal.metals.CompilerOffsetParams import scala.language.unsafeNulls +import scala.meta.internal.metals.CompilerOffsetParams abstract class BaseSignatureHelpSuite extends BasePCSuite: def checkDoc( @@ -27,6 +28,10 @@ abstract class BaseSignatureHelpSuite extends BasePCSuite: ) .get() val out = new StringBuilder() + + // this is default SignatureHelp value which should only be returned on crash + assert(result != new SignatureHelp()) + if (result != null) { result.getSignatures.asScala.zipWithIndex.foreach { case (signature, i) => if (includeDocs) { @@ -38,10 +43,7 @@ abstract class BaseSignatureHelpSuite extends BasePCSuite: out .append(signature.getLabel) .append("\n") - if ( - result.getActiveSignature == i && result.getActiveParameter != null && signature.getParameters - .size() > 0 - ) { + if (result.getActiveSignature == i && result.getActiveParameter != null && signature.getParameters.size() > 0) { val param = signature.getParameters.get(result.getActiveParameter) val label = param.getLabel.getLeft() /* We need to find the label of the active parameter and show ^ at that spot diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala index c419ce3946d9..bcfd399c7c4a 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala @@ -16,14 +16,14 @@ class CompletionDocSuite extends BaseCompletionSuite: MockDocumentation("java/lang/String#join().", "join", Seq(), Seq("delimiter", "elements")), MockDocumentation("java/lang/String#substring().", "substring", Seq(), Seq("beginIndex")), MockDocumentation("java/lang/String#substring(+1).", "substring", Seq(), Seq("beginIndex", "endIndex")), - ScalaMockDocumentation("scala/collection/Iterator#sliding().", "sliding", List(MockParam("size"), MockParam("step", "1"))), - ScalaMockDocumentation("scala/collection/immutable/TreeMap#insert().", "insert", List(MockParam("key"), MockParam("value"))), + ScalaMockDocumentation("scala/collection/Iterator#sliding().", "sliding", List(), List(MockParam("size"), MockParam("step", "1"))), + ScalaMockDocumentation("scala/collection/immutable/TreeMap#insert().", "insert", List(), List(MockParam("key"), MockParam("value"))), ScalaMockDocumentation("scala/Option#isDefined().", "isDefined"), ScalaMockDocumentation("scala/util/DynamicVariable#", "DynamicVariable"), ScalaMockDocumentation("scala/util/DynamicVariable.", "DynamicVariable"), - ScalaMockDocumentation("scala/io/Source#reportWarning().", "reportWarning", List(MockParam("pos"), MockParam("msg"), MockParam("out", "Console.out"))), + ScalaMockDocumentation("scala/io/Source#reportWarning().", "reportWarning", List(), List(MockParam("pos"), MockParam("msg"), MockParam("out", "Console.out"))), ScalaMockDocumentation("scala/Predef.println().", "println"), - ScalaMockDocumentation("scala/Predef.println(+1).", "println", List(MockParam("x"))), + ScalaMockDocumentation("scala/Predef.println(+1).", "println", List(), List(MockParam("x"))), ScalaMockDocumentation("scala/Predef.", "Predef"), ScalaMockDocumentation("scala/runtime/stdLibPatches/Predef.", "Predef"), ScalaMockDocumentation("scala/util/control/Exception.Catch#", "Catch"), diff --git a/presentation-compiler/test/dotty/tools/pc/tests/edit/ConvertToNamedArgumentsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/edit/ConvertToNamedArgumentsSuite.scala index 5285be83b537..359a63442408 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/edit/ConvertToNamedArgumentsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/edit/ConvertToNamedArgumentsSuite.scala @@ -6,7 +6,6 @@ import java.util.concurrent.ExecutionException import scala.meta.internal.jdk.CollectionConverters.* import scala.meta.internal.metals.CompilerOffsetParams import scala.meta.internal.pc.CodeActionErrorMessages -import scala.meta.pc.DisplayableException import scala.language.unsafeNulls import dotty.tools.pc.base.BaseCodeActionSuite diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala index 06c629467166..fc9b6835e319 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala @@ -10,10 +10,10 @@ class HoverDocSuite extends BaseHoverSuite: override protected def mockEntries: MockEntries = new MockEntries: override def documentations: Set[SymbolDocumentation] = Set( - ScalaMockDocumentation("java/lang/String#substring().", "substring", List(MockParam("beginIndex"))), + ScalaMockDocumentation("java/lang/String#substring().", "substring", List(), List(MockParam("beginIndex"))), ScalaMockDocumentation("java/util/Collections#emptyList().", "emptyList"), - ScalaMockDocumentation("_empty_/Alpha.apply().", "apply", List(MockParam("x"))), - ScalaMockDocumentation("_empty_/Alpha#", "init", List(MockParam("x"))), + ScalaMockDocumentation("_empty_/Alpha.apply().", "apply", List(), List(MockParam("x"))), + ScalaMockDocumentation("_empty_/Alpha#", "init", List(), List(MockParam("x"))), ScalaMockDocumentation("scala/collection/LinearSeqOps#headOption().", "headOption"), ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverNamedArgSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverNamedArgSuite.scala index f92ba9933be4..182f8e1e0644 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverNamedArgSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverNamedArgSuite.scala @@ -11,7 +11,7 @@ class HoverNamedArgSuite extends BaseHoverSuite: override protected def mockEntries: MockEntries = new MockEntries: override def documentations: Set[SymbolDocumentation] = Set( - ScalaMockDocumentation("a/b.foo().(named)", "foo", List(MockParam("named"))) + ScalaMockDocumentation("a/b.foo().(named)", "foo", List(), List(MockParam("named"))) ) @Test def `named` = diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala index 7d36dc6ce23d..09b22559d35c 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala @@ -26,13 +26,13 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: MockDocumentation("java/io/File#``(+1).", "", Seq(), Seq("parent", "child")), MockDocumentation("java/io/File#``(+2).", "", Seq(), Seq("parent", "child")), MockDocumentation("java/io/File#``(+3).", "", Seq(), Seq("uri")), - ScalaMockDocumentation("java/util/Collections#singleton().", "singleton", List(MockParam("o"))), + ScalaMockDocumentation("java/util/Collections#singleton().", "singleton", List(MockParam("T")), List(MockParam("o"))), ScalaMockDocumentation("scala/Some#", "Some"), - ScalaMockDocumentation("scala/Option#fold().", "fold", List(MockParam("ifEmpty"), MockParam("f"))), - ScalaMockDocumentation("scala/Option.apply().", "apply", List(MockParam("x"))), - ScalaMockDocumentation("scala/collection/immutable/List#map().", "map", List(MockParam("f"))), - ScalaMockDocumentation("scala/collection/LinearSeqOps#foldLeft().", "foldLeft", List(MockParam("z"), MockParam("op"))), - ScalaMockDocumentation("scala/util/control/Exception.Catch#", "Catch", List(MockParam("pf"), MockParam("fin"), MockParam("rethrow"))) + ScalaMockDocumentation("scala/Option#fold().", "fold", List(MockParam("B")), List(MockParam("ifEmpty"), MockParam("f"))), + ScalaMockDocumentation("scala/Option.apply().", "apply", List(), List(MockParam("x"))), + ScalaMockDocumentation("scala/collection/immutable/List#map().", "map", List(MockParam("B")), List(MockParam("f"))), + ScalaMockDocumentation("scala/collection/LinearSeqOps#foldLeft().", "foldLeft", List(MockParam("B")), List(MockParam("z"), MockParam("op"))), + ScalaMockDocumentation("scala/util/control/Exception.Catch#", "Catch", List(), List(MockParam("pf"), MockParam("fin"), MockParam("rethrow"))) ) @Test def `curry` = @@ -45,6 +45,7 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: s"""Found documentation for scala/Option#fold(). |fold[B](ifEmpty: => B)(f: Int => B): B | ^^^^^^^^^^^ + | @param B Found documentation for type param B | @param ifEmpty Found documentation for param ifEmpty | @param f Found documentation for param f """.stripMargin @@ -60,6 +61,7 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: s"""|Found documentation for scala/Option#fold(). |fold[B](ifEmpty: => B)(f: Int => B): B | ^^^^^^^^^^^^^ + | @param B Found documentation for type param B | @param ifEmpty Found documentation for param ifEmpty | @param f Found documentation for param f |""".stripMargin @@ -77,6 +79,7 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: """|Found documentation for scala/collection/LinearSeqOps#foldLeft(). |foldLeft[B](z: B)(op: (B, Int) => B): B | ^^^^^^^^^^^^^^^^^ + | @param B Found documentation for type param B | @param z Found documentation for param z | @param op Found documentation for param op |""".stripMargin @@ -106,6 +109,7 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: """|Found documentation for scala/collection/immutable/List#map(). |map[B](f: Int => B): List[B] | ^^^^^^^^^^^ + | @param B Found documentation for type param B | @param f Found documentation for param f |""".stripMargin ) @@ -134,6 +138,7 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: """|Found documentation for java/util/Collections#singleton(). |singleton[T](o: T): java.util.Set[T] | ^^^^ + | @param T Found documentation for type param T | @param o Found documentation for param o |""".stripMargin ) @@ -146,8 +151,8 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: |} """.stripMargin, """|Found documentation for scala/util/control/Exception.Catch# - |Catch[T](pf: scala.util.control.Exception.Catcher[T], fin: Option[scala.util.control.Exception.Finally], rethrow: Throwable => Boolean) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Catch[T](pf: Catcher[T], fin: Option[Finally], rethrow: Throwable => Boolean) + | ^^^^^^^^^^^^^^ | @param pf Found documentation for param pf | @param fin Found documentation for param fin | @param rethrow Found documentation for param rethrow @@ -161,9 +166,9 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: | new java.io.File(@@) |} """.stripMargin, - """|File(uri: java.net.URI) - | ^^^^^^^^^^^^^^^^^ - |File(parent: java.io.File, child: String) + """|File(uri: URI) + | ^^^^^^^^ + |File(parent: File, child: String) |File(parent: String, child: String) |File(pathname: String) |""".stripMargin diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala new file mode 100644 index 000000000000..15546d086033 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala @@ -0,0 +1,518 @@ +package dotty.tools.pc.tests.signaturehelp + +import dotty.tools.pc.base.BaseSignatureHelpSuite + +import org.junit.Test +import org.junit.Ignore +import java.nio.file.Path + +class SignatureHelpInterleavingSuite extends BaseSignatureHelpSuite: + + override protected def scalacOptions(classpath: Seq[Path]): Seq[String] = + List("-language:experimental.clauseInterleaving") + + @Test def `proper-position-1` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[@@Int](1)[String]("1") + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + + @Test def `proper-position-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](@@1)[String]("1") + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `proper-position-3` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](1)[@@String]("1") + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `proper-position-4` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](1)[String](@@"1") + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `not-fully-applied-1` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[@@Int] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + + @Test def `not-fully-applied-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](@@1) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `not-fully-applied-3` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](1)[@@String] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `error` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int][@@String] + """.stripMargin, + "" + ) + + @Test def `inferred-type-param-1` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair(1@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `inferred-type-param-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair(1)[String@@] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `inferred-type-param-3` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair(1)[String]("1"@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `inferred-type-param-4` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair(1)("1"@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-current-param-check` = + check( + """ + |object Test: + | def pair[A](a: A): A = ??? + | pair[@@] + """.stripMargin, + """ + |pair[A](a: A): A + | ^ + |""".stripMargin + ) + + @Test def `empty-current-param-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-current-param-3` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](1)[@@] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `empty-current-param-4` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[Int](1)[String](@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-current-inferred-1` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair(@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-current-inferred-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair(1)(@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-previous-1` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[](@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-previous-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[]()[@@] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `empty-previous-3` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[]()[](@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-previous-4` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[]()[](11@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-previous-5` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[](5@@1) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `empty-previous-6` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[String]()[@@] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `empty-previous-7` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[String]()[Int](@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `error-previous-1` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[String](52)[@@] + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^ + |""".stripMargin + ) + + @Test def `error-previous-2` = + check( + """ + |object Test: + | def pair[A](a: A)[B](b: B): (A, B) = (a, b) + | pair[String](52)[Int](""@@) + """.stripMargin, + """ + |pair[A](a: A)[B](b: B): (A, B) + | ^^^^ + |""".stripMargin + ) + + @Test def `complex-1` = + check( + """ + |object Test: + | def foo[A](using a: A)(b: List[A])[C <: a.type, D](cd: (C, D))[E]: Foo[A, B, C, D, E] + | foo[Int](using 1)(List(1, 2, 3))(@@) + """.stripMargin, + """ + |foo[A](using a: A)(b: List[A])[C <: a.type, D](cd: (C, D))[E]: Foo[A, B, C, D, E] + | ^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `complex-2` = + check( + """ + |object Test: + | def foo[A](using a: A)(b: List[A])[C <: a.type, D](cd: (C, D))[E]: Foo[A, B, C, D, E] + | foo[Int](using 1)(List(1, 2, 3))((1, 2))[@@] + """.stripMargin, + """ + |foo[A](using a: A)(b: List[A])[C <: a.type, D](cd: (C, D))[E]: Foo[A, B, C, D, E] + | ^ + |""".stripMargin + ) + + @Ignore("""Clause interleaving is still experimental. It lifts this tree into series of anonymous functions, which all have the same span. + It requires further investigation to determine whether this is a bug in the compiler.""") + @Test def `clause-interleaving-empty` = + check( + """|object M: + | def test[X](x: X)[Y](y: Y): (X, Y)= ??? + | test[@@] + |""".stripMargin, + """|test[X](x: X)[Y](y: Y): (X, Y) + | ^ + |""".stripMargin + ) + + @Ignore("""Clause interleaving is still experimental. It lifts this tree into series of anonymous functions, which all have the same span. + It requires further investigation to determine whether this is a bug in the compiler.""") + @Test def `more-interleaved-params-1` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[@@] + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^ + |""".stripMargin + ) + + @Test def `more-interleaved-params-2` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](@@) + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^^^^ + |""".stripMargin + ) + + @Ignore("""Clause interleaving is still experimental. It lifts this tree into series of anonymous functions, which all have the same span. + It requires further investigation to determine whether this is a bug in the compiler.""") + @Test def `more-interleaved-params-3` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)[@@] + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^ + |""".stripMargin + ) + + @Test def `more-interleaved-params-4` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)[String](@@) + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^^^^ + |""".stripMargin + ) + + @Ignore("""Clause interleaving is still experimental. It lifts this tree into series of anonymous functions, which all have the same span. + It requires further investigation to determine whether this is a bug in the compiler.""") + @Test def `more-interleaved-params-5` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)[String]("1")[@@] + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^ + |""".stripMargin + ) + + @Test def `more-interleaved-params-6` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)[String]("1")[Int](@@) + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^^^^ + |""".stripMargin + ) + + @Test def `more-interleaved-params-7` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)[String]("1")[Int](2)[@@] + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^ + |""".stripMargin + ) + + @Test def `more-interleaved-params-8` = + check( + """|object M: + | def test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)[String]("1")[Int](2)[String](@@) + |""".stripMargin, + """|test[A](a: A)[B](b: B)[C](c: C)[D](d: D): Int + | ^^^^ + |""".stripMargin + ) + + @Test def `interleaving-with-implicit` = + check( + """|object M: + | def test[A](a: A)(using Int)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)(using 5)[String]("1")[Int](@@) + |""".stripMargin, + """|test[A](a: A)(using Int)[B](b: B)[C](c: C)[D](d: D): Int + | ^^^^ + |""".stripMargin + ) + + @Test def `interleaving-with-implicit-recovery` = + check( + """|object M: + | def test[A](a: A)(using Int)[B](b: B)[C](c: C)[D](d: D): Int = ??? + | test[Int](1)(5)[String]("1")[Int](@@) + |""".stripMargin, + """|test[A](a: A)(using Int)[B](b: B)[C](c: C)[D](d: D): Int + | ^^^^ + |""".stripMargin + ) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpNamedArgsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpNamedArgsSuite.scala new file mode 100644 index 000000000000..1906e12a0254 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpNamedArgsSuite.scala @@ -0,0 +1,300 @@ +package dotty.tools.pc.tests.signaturehelp + +import dotty.tools.pc.base.BaseSignatureHelpSuite + +import org.junit.Test + +class SignatureHelpNamedArgsSuite extends BaseSignatureHelpSuite: + + + @Test def `new-named-param-style-1` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-double-space` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-before-equal` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, paramB @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-before-equal-something-after` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, paramB @@) + | def test: Unit = () + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-at-equal` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, paramB =@@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-after-equal` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, paramB = 1@@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-2` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 1, @@) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramC: Int], [paramD: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-3` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 1, paramC = 2, paramD = 3, @@) + |""".stripMargin, + """|method([paramB: Int], [paramC: Int], [paramD: Int], [paramA: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-4` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(1, 2, @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-5` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(1, paramB = 2, @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-6` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, 2, @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-7` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramA = 1, paramB = 2, @@) + |""".stripMargin, + """|method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-8` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 2, paramA = 1, @@) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramC: Int], [paramD: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-9` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramC = 3, paramA = 1, @@) + |""".stripMargin, + """|method([paramC: Int], [paramA: Int], [paramB: Int], [paramD: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-10` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramC = 3, @@ ,paramA = 1) + |""".stripMargin, + """|method([paramC: Int], [paramB: Int], [paramA: Int], [paramD: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `new-named-param-style-11` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramC = 3, paramB = 1, @@ ,paramA = 1) + |""".stripMargin, + """|method([paramC: Int], [paramB: Int], [paramD: Int], [paramA: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-param-before-first` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(@@paramC = 3) + |""".stripMargin, + """|method([paramC: Int], [paramA: Int], [paramB: Int], [paramD: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-param-inside-reordered-last-arg` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 3, paramA = 1, @@paramD = 3, paramC = 1) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramD: Int], [paramC: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-param-inside-reorderded-last-arg-1` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 3, paramA = 1, p@@aramD = 3, paramC = 1) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramD: Int], [paramC: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-param-before-reorderded-last-arg-1` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 3, paramA = 1,@@ paramD = 3, paramC = 1) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramD: Int], [paramC: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-param-properly-order-1` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(par@@amB = 3, paramA = 1, paramD = 3, paramC = 1) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramD: Int], [paramC: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-param-properly-order-2` = + check( + """|object O: + | def method(paramA: Int, paramB: Int, paramC: Int, paramD: Int): Unit = ??? + | method(paramB = 3, par@@amA = 1, paramD = 3, paramC = 1) + |""".stripMargin, + """|method([paramB: Int], [paramA: Int], [paramD: Int], [paramC: Int]): Unit + | ^^^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-with-type-args` = + check( + """|object Main: + | def test[T, F](aaa: Int, bbb: T, ccc: F): T = ??? + | val x = test(1, ccc = 2, b@@) + |""".stripMargin, + """|test[T, F](aaa: Int, [ccc: F], [bbb: T]): T + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-newline` = + check( + """|object Main: + | def test2(aaa: Int, bbb: Int, ccc: Int): Int = ??? + | val x = test2( + | 1, + | @@ + | ) + |""".stripMargin, + """|test2(aaa: Int, bbb: Int, ccc: Int): Int + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-before-comma` = + check( + """|object Main: + | def test2(aaa: Int, bbb: Int, ccc: Int): Int = ??? + | val x = test2(aaa = 2@@, ccc = 1) + |""".stripMargin, + """|test2(aaa: Int, [ccc: Int], [bbb: Int]): Int + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `named-on-whitespaces-between-args` = + check( + """|object Main: + | def test2(aaa: Int, bbb: Int, ccc: Int): Int = ??? + | val x = test2(aaa = 2, @@ ccc = 1, bbb = 3) + | + |""".stripMargin, + """|test2(aaa: Int, [ccc: Int], [bbb: Int]): Int + | ^^^^^^^^^^ + |""".stripMargin + ) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpPatternSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpPatternSuite.scala index f8a368a5ea3a..d01145510367 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpPatternSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpPatternSuite.scala @@ -202,23 +202,6 @@ class SignatureHelpPatternSuite extends BaseSignatureHelpSuite: |""".stripMargin ) - @Test def `pat4` = - check( - """ - |object & { - | def unapply[A](a: A): Some[(A, A)] = Some((a, a)) - |} - |object a { - | "" match { - | case "" & s@@ - | } - |} - """.stripMargin, - """|(String, String) - | ^^^^^^ - |""".stripMargin - ) - @Test def `pat5` = check( """ @@ -255,6 +238,24 @@ class SignatureHelpPatternSuite extends BaseSignatureHelpSuite: |""".stripMargin ) + @Test def `shortened` = + check( + """ + |object Test { + | def unapply(command: java.io.File): Option[java.io.File] = { + | Some(Some(1)) + | } + | + | "" match { + | case Test(@@) => + | } + |} + """.stripMargin, + """|(File) + | ^^^^ + |""".stripMargin + ) + @Test def `pat-negative` = check( """ diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala index 906a0bd9b72e..896cbdb3cad2 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala @@ -66,7 +66,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: """|Random() |Random(seed: Int) |Random(seed: Long) - |Random(self: java.util.Random) + |Random(self: Random) |""".stripMargin ) @@ -103,9 +103,9 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new File(@@) |} """.stripMargin, - """|File(x$0: java.net.URI) - | ^^^^^^^^^^^^^^^^^ - |File(x$0: java.io.File, x$1: String) + """|File(x$0: URI) + | ^^^^^^^^ + |File(x$0: File, x$1: String) |File(x$0: String, x$1: String) |File(x$0: String) |""".stripMargin @@ -118,9 +118,9 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new java.io.File(@@) |} """.stripMargin, - """|File(x$0: java.net.URI) - | ^^^^^^^^^^^^^^^^^ - |File(x$0: java.io.File, x$1: String) + """|File(x$0: URI) + | ^^^^^^^^ + |File(x$0: File, x$1: String) |File(x$0: String, x$1: String) |File(x$0: String) |""".stripMargin @@ -196,7 +196,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: check( """ |object a { - | List(1, 2@@ + | List(1, 2@@) |} """.stripMargin, """|apply[A](elems: A*): List[A] @@ -323,9 +323,9 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | } yield k |} """.stripMargin, - """|to(end: Int): scala.collection.immutable.Range.Inclusive + """|to(end: Int): Inclusive | ^^^^^^^^ - |to(end: Int, step: Int): scala.collection.immutable.Range.Inclusive + |to(end: Int, step: Int): Inclusive |""".stripMargin, stableOrder = false ) @@ -388,7 +388,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | | """.stripMargin, - """|apply(viewId: String, nodeUri: String, label: String, command: String, icon: String, tooltip: String, collapseState: String): TreeViewNode + """|apply(viewId: String, nodeUri: String, label: String, [collapseState: String], [command: String], [icon: String], [tooltip: String]): TreeViewNode | ^^^^^^^^^^^^^^ |""".stripMargin ) @@ -424,7 +424,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | | """.stripMargin, - """|apply(viewId: String, nodeUri: String, label: String, command: String, collapseState: String): TreeViewNode + """|apply(viewId: String, nodeUri: String, label: String, [collapseState: String], [command: String]): TreeViewNode | ^^^^^^^^^^^^^^ |""".stripMargin ) @@ -437,8 +437,8 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | User(age = 1, @@) |} """.stripMargin, - """|apply(name: String, age: Int): User - | ^^^^^^^^ + """|apply([age: Int], [name: String]): User + | ^^^^^^^^^^^^^^ |""".stripMargin ) @@ -477,8 +477,8 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | def x = user(str@@eet = 42, name = "", age = 2) |} """.stripMargin, - """|user(name: String, age: Int, street: Int): Int - | ^^^^^^^^^^^ + """|user([street: Int], [name: String], [age: Int]): Int + | ^^^^^^^^^^^^^ |user(name: String, age: Int): Int |""".stripMargin ) @@ -502,9 +502,8 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new scala.util.control.Exception.Catch(@@) |} """.stripMargin, - // TODO short names are not supported yet - """|Catch[T](pf: scala.util.control.Exception.Catcher[T], fin: Option[scala.util.control.Exception.Finally], rethrow: Throwable => Boolean) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + """|Catch[T](pf: Catcher[T], fin: Option[Finally], rethrow: Throwable => Boolean) + | ^^^^^^^^^^^^^^ |""".stripMargin ) @@ -515,7 +514,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new java.util.HashMap[String, Int]().computeIfAbsent(@@) |} """.stripMargin, - // TODO short names are not supported yet + // This is the correct result, as there is a conflict at Function: scala.Function and java.util.function.Function """|computeIfAbsent(x$0: String, x$1: java.util.function.Function[? >: String, ? <: Int]): Int | ^^^^^^^^^^^ |""".stripMargin @@ -650,9 +649,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | identity(42)@@ |} |""".stripMargin, - """|identity[A](x: A): A - | ^^^^ - |""".stripMargin + "" ) @Test def `off-by-one2` = @@ -675,7 +672,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: |} |""".stripMargin, """|fold[B](ifEmpty: => B)(f: Int => B): B - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^ |""".stripMargin ) @@ -763,24 +760,13 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: """|object O: | implicit class Test[T](xs: List[T]): | def test(x: T): List[T] = ??? - | List(1,2,3).test(@@""".stripMargin, + | List(1,2,3).test(s@@)""".stripMargin, """|test(x: Int): List[Int] | ^^^^^^ |""".stripMargin ) @Test def `instantiated-type-var-old-ext-2` = - check( - """|object O: - | implicit class Test[T](xs: List[T]): - | def test(x: T): List[T] = ??? - | List(1,2,3).test(s@@""".stripMargin, - """|test(x: Int): List[Int] - | ^^^^^^ - |""".stripMargin - ) - - @Test def `instantiated-type-var-old-ext-3` = check( """|object O: | implicit class Test[T](xs: List[T]): @@ -791,7 +777,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: |""".stripMargin ) - @Test def `instantiated-type-var-old-ext-4` = + @Test def `instantiated-type-var-old-ext-3` = check( """|object O: | implicit class Test[T](xs: List[T]): @@ -804,7 +790,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: |""".stripMargin ) - @Test def `instantiated-type-var-old-ext-5` = + @Test def `instantiated-type-var-old-ext-4` = check( """|object O: | implicit class Test[T](xs: List[T]): @@ -818,7 +804,7 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: |""".stripMargin ) - @Test def `instantiated-type-var-old-ext-6` = + @Test def `instantiated-type-var-old-ext-5` = check( """|object O: | implicit class Test[T](xs: List[T]): @@ -829,3 +815,561 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | ^^^^^^ |""".stripMargin ) + + @Test def `multiline-before` = + check( + """|object Main { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(@@ + | fst = "abc", + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `multiline-after-first` = + check( + """|object Main { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment( + | fst = "abc", @@ + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `multiline-between-first-and-second-a` = + check( + """|object Main { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment( + | fst = "abc" + | @@ + | + | ,snd = 1 + | ) + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^^^^ + |""".stripMargin + ) + + @Test def `multiline-between-first-and-second-b` = + check( + """|object Main { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment( + | fst = "abc", + | @@ + | + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `multiline-end` = + check( + """|object Main { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment( + | fst = "abc", + | snd = 1 + | @@) + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `type-var-multiline-before` = + check( + """|object Main { + | def deployment[A, B]( + | fst: A, + | snd: B, + | ): Option[Int] = ??? + | val abc = deployment[@@ + Int, + | String, + | ]( + | fst = "abc", + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment[A, B](fst: A, snd: B): Option[Int] + | ^ + |""".stripMargin + ) + + @Test def `type-var-multiline-after` = + check( + """|object Main { + | def deployment[A, B]( + | fst: A, + | snd: B, + | ): Option[Int] = ??? + | val abc = deployment[ + | Int, @@ + | String, + | ]( + | fst = "abc", + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment[A, B](fst: A, snd: B): Option[Int] + | ^ + |""".stripMargin + ) + + @Test def `type-var-multiline-between-first-and-second-a` = + check( + """|object Main { + | def deployment[A, B]( + | fst: A, + | snd: B, + | ): Option[Int] = ??? + | val abc = deployment[ + | Int + | @@ + | + | ,String + | ]( + | fst = "abc", + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment[A, B](fst: A, snd: B): Option[Int] + | ^ + |""".stripMargin + ) + + @Test def `type-var-multiline-between-first-and-second-b` = + check( + """|object Main { + | def deployment[A, B]( + | fst: A, + | snd: B, + | ): Option[Int] = ??? + | val abc = deployment[ + | Int, + | @@ + | + | String, + | ]( + | fst = "abc", + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment[A, B](fst: A, snd: B): Option[Int] + | ^ + |""".stripMargin + ) + + @Test def `type-var-multiline-end` = + check( + """|object Main { + | def deployment[A, B]( + | fst: A, + | snd: B, + | ): Option[Int] = ??? + | val abc = deployment[ + | String, + | Int, + | @@]( + | fst = "abc", + | snd = 1 + | ) + |} + |""".stripMargin, + """|deployment[A, B](fst: A, snd: B): Option[Int] + | ^ + |""".stripMargin + ) + + @Test def `dont-show-directly-after-parenthesis` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, 2)@@ + |} + |""".stripMargin, + "" + ) + + @Test def `dont-show-directly-after-parenthesis-2` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, 2)@@ + |""".stripMargin, + "" + ) + + @Test def `dont-show-directly-when-unclosed` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, (2 + 1)@@ + |} + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-1` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, 2) @@ + |} + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-2` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, 2) @@ + |} + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-newline` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, 2) + |@@ + |} + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-newline-last-statement` = + check( + """|object Main: + | def test(a: Int, b: Int): Int = ??? + | test(1, 2) + | + |@@ + | + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-newline-last-statement-unclosed-1` = + check( + """|object Main: + | def test(a: Int, b: Int): Int = ??? + | test(1, 2 + | + |@@ + | + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-newline-last-statement-unclosed-2` = + check( + """|object Main: + | def test(a: Int, b: Int): Int = ??? + | test(1, (1 + 2) + | + |@@ + | + |""".stripMargin, + "" + ) + + @Test def `dont-show-after-parenthesis-unclosed-2` = + check( + """|object Main { + | def test(a: Int, b: Int): Int = ??? + | test(1, 2 + | + | @@ + |} + |""".stripMargin, + "" + ) + + @Test def `select-arg-detection` = + check( + """|object Main: + | object Foo: + | case class Test(x: Int) + | def test(a: Foo.Test, b: Foo.Test): Int = ??? + | test(Foo.Test(1), @@) + |""".stripMargin, + """|test(a: Test, b: Test): Int + | ^^^^^^^ + |""".stripMargin + ) + + @Test def `singature-help-works-in-select` = + check( + """|object Main: + | object Foo: + | class Test(x: Int, y: Int) + | new Foo.Test(1, @@) + |""".stripMargin, + """|Test(x: Int, y: Int) + | ^^^^^^ + |""".stripMargin + ) + + @Test def `curried-help-works-in-select` = + check( + """|object Main: + | def test(xxx: Int, yyy: Int)(zzz: Int): Int = ??? + | test(yyy = 5, xxx = 7)(@@) + |""".stripMargin, + """|test([yyy: Int], [xxx: Int])(zzz: Int): Int + | ^^^^^^^^ + |""".stripMargin + ) + + @Test def `no-signature-help-for-parameterless-method` = + check( + """|object Main: + | def test: Int = ??? + | test(@@) + |""".stripMargin, + "" + ) + + @Test def `show-methods-returning-tuples` = + check( + """|object Main: + | def test(): (Int, Int) = ??? + | test(@@) + |""".stripMargin, + "test(): (Int, Int)" + ) + + @Test def `show-methods-returning-tuples-2` = + check( + """|object Main: + | def test(x: Int): (Int, Int) = ??? + | test(@@) + |""".stripMargin, + """|test(x: Int): (Int, Int) + | ^^^^^^ + |""".stripMargin + ) + + @Test def `dont-show-tuples-application` = + check( + """|object Main: + | (1, @@) + |""".stripMargin, + "" + ) + + // Improvement would be to create synthetic signature help showing + // add(x: Int)(y: Int): Int + @Test def `dont-show-functionN` = + check( + """|object Main: + | val add = (x: Int) => (y: Int) => x + y + | add(@@) + |""".stripMargin, + "" + ) + + @Test def `dont-show-functionN-2` = + check( + """|object Main: + | val add = (x: Int) => (y: Int) => x + y + | add(1, @@) + |""".stripMargin, + "" + ) + + @Test def `type-param-start` = + check( + """|object Main: + | def test[A](x: A): A = ??? + | test[@@] + |""".stripMargin, + """|test[A](x: A): A + | ^ + |""".stripMargin + ) + + @Test def `error-recovery-1` = + check( + """|object Main: + | def test[A](x: A): Foo[A] = ??? + | test[@@] + |""".stripMargin, + """|test[A](x: A): Foo[A] + | ^ + |""".stripMargin + ) + + @Test def `error-recovery-2` = + check( + """|object Main: + | def test[A](x: A): Foo[A] = ??? + | test[Int](@@) + |""".stripMargin, + """|test[A](x: A): Foo[A] + | ^^^^ + |""".stripMargin + ) + + @Test def `type-param-shortening` = + check( + """|object M: + | def test[T <: java.io.File](x: Int): Int = ??? + | test(@@) + |""".stripMargin, + """|test[T <: File](x: Int): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `implicit-param` = + check( + """|object M: + | trait Context + | def test(x: Int)(using ctx: Context): Int = ??? + | test(@@) + |""".stripMargin, + """|test(x: Int)(using ctx: Context): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `context-param` = + check( + """|object M: + | def test(x: Int, y: Int = 7)(z: Int ?=> Int): Int = ??? + | test(@@) + |""".stripMargin, + """|test(x: Int, y: Int)(z: (Int) ?=> Int): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `empty-implicit-params` = + check( + """|object M: + | def test(x: Int)(using String): Int = ??? + | test(1)(@@) + |""".stripMargin, + """|test(x: Int)(using String): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `multiple-implicits-1` = + check( + """|object M: + | def a(using Int)(using String): Int = ??? + | a(@@) + |""".stripMargin, + """|a(using Int)(using String): Int + | ^^^ + |""".stripMargin + ) + + + @Test def `multiple-implicits-2` = + check( + """|object M: + | def a(using Int)(using String): Int = ??? + | a(using 5)(@@) + |""".stripMargin, + """|a(using Int)(using String): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `multiple-implicits-3` = + check( + """|object M: + | def a(using Int)(using String)(x: Int): Int = ??? + | a(@@) + |""".stripMargin, + """|a(using Int)(using String)(x: Int): Int + | ^^^ + |""".stripMargin + ) + + @Test def `multiple-implicits-4` = + check( + """|object M: + | def a(using Int)(using String)(x: Int): Int = ??? + | a(using 5)(@@) + |""".stripMargin, + """|a(using Int)(using String)(x: Int): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `multiple-implicits-error-1` = + check( + """|object M: + | def a(using Int)(using String)(x: Int): Int = ??? + | a(5)(@@) + |""".stripMargin, + """| + |a(using Int)(using String)(x: Int): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `multiple-implicits-error-2` = + check( + """|object M: + | def a(using Int)(using String)(x: Int): Int = ??? + | a(5)(@@) + |""".stripMargin, + """|a(using Int)(using String)(x: Int): Int + | ^^^^^^ + |""".stripMargin + ) + + @Test def `dont-crash-at-last-position` = + check( + """|object M: + | def test(x: Int): Int = ??? + | test(@@""".stripMargin, + "" + ) diff --git a/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala b/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala index 05cd2cb8c124..1c0c35c0c71e 100644 --- a/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala +++ b/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala @@ -28,6 +28,7 @@ abstract class MockEntries: def apply( symbol: String, displayName: String, + typeParams: List[MockParam] = Nil, params: List[MockParam] = Nil ): SymbolDocumentation = ScalaSymbolDocumentation( @@ -35,7 +36,14 @@ abstract class MockEntries: displayName, s"Found documentation for $symbol", "", - Nil.asJava, + typeParams + .map: param => + ScalaSymbolDocumentation( + param.name, + param.name, + s"Found documentation for type param ${param.name}\n", + ) + .asJava, params .map(param => ScalaSymbolDocumentation( @@ -43,8 +51,6 @@ abstract class MockEntries: param.name, s"Found documentation for param ${param.name}\n", param.defaultValue, - Nil.asJava, - Nil.asJava ) ) .asJava