Skip to content

Commit b9cd807

Browse files
authored
bugfix: Completions for named args in wrong order (#18702)
connected to scalameta/metals#5728
1 parent e196dec commit b9cd807

File tree

3 files changed

+118
-11
lines changed

3 files changed

+118
-11
lines changed

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ class Completions(
5656

5757
val coursierComplete = new CoursierComplete(BuildInfo.scalaVersion)
5858

59+
private lazy val adjustedPath = Completion.resolveTypedOrUntypedPath(path, pos)
5960
private lazy val completionMode =
60-
val adjustedPath = Completion.resolveTypedOrUntypedPath(path, pos)
6161
val mode = Completion.completionMode(adjustedPath, pos)
6262
path match
6363
case Literal(Constant(_: String)) :: _ => Mode.Term // literal completions
@@ -451,6 +451,7 @@ class Completions(
451451
val args = NamedArgCompletions.contribute(
452452
pos,
453453
path,
454+
adjustedPath,
454455
indexedContext,
455456
config.isCompletionSnippetsEnabled()
456457
)

presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package dotty.tools.pc.completions
22

33
import scala.util.Try
44

5+
import dotty.tools.dotc.ast.NavigateAST
56
import dotty.tools.dotc.ast.Trees.ValDef
67
import dotty.tools.dotc.ast.tpd.*
8+
import dotty.tools.dotc.ast.untpd
79
import dotty.tools.dotc.core.Constants.Constant
810
import dotty.tools.dotc.core.ContextOps.localContext
911
import dotty.tools.dotc.core.Contexts.Context
@@ -12,7 +14,10 @@ import dotty.tools.dotc.core.Flags
1214
import dotty.tools.dotc.core.Flags.Method
1315
import dotty.tools.dotc.core.NameKinds.DefaultGetterName
1416
import dotty.tools.dotc.core.Names.Name
17+
import dotty.tools.dotc.core.StdNames.*
18+
import dotty.tools.dotc.core.SymDenotations.NoDenotation
1519
import dotty.tools.dotc.core.Symbols
20+
import dotty.tools.dotc.core.Symbols.NoSymbol
1621
import dotty.tools.dotc.core.Symbols.Symbol
1722
import dotty.tools.dotc.core.Types.AndType
1823
import dotty.tools.dotc.core.Types.AppliedType
@@ -33,8 +38,9 @@ object NamedArgCompletions:
3338
def contribute(
3439
pos: SourcePosition,
3540
path: List[Tree],
41+
untypedPath: => List[untpd.Tree],
3642
indexedContext: IndexedContext,
37-
clientSupportsSnippets: Boolean
43+
clientSupportsSnippets: Boolean,
3844
)(using ctx: Context): List[CompletionValue] =
3945
path match
4046
case (ident: Ident) :: ValDef(_, _, _) :: Block(_, app: Apply) :: _
@@ -43,7 +49,7 @@ object NamedArgCompletions:
4349
Some(ident),
4450
app,
4551
indexedContext,
46-
clientSupportsSnippets
52+
clientSupportsSnippets,
4753
)
4854
case (ident: Ident) :: rest =>
4955
def getApplyForContextFunctionParam(path: List[Tree]): Option[Apply] =
@@ -63,9 +69,29 @@ object NamedArgCompletions:
6369
Some(ident),
6470
app,
6571
indexedContext,
66-
clientSupportsSnippets
72+
clientSupportsSnippets,
6773
)
6874
contribution.getOrElse(Nil)
75+
case (app: Apply) :: _ =>
76+
/**
77+
* def foo(aaa: Int, bbb: Int, ccc: Int) = ???
78+
* val x = foo(
79+
* bbb = 123,
80+
* ccc = 123,
81+
* @@
82+
* )
83+
* In this case, typed path doesn't contain already provided arguments
84+
*/
85+
untypedPath match
86+
case (ident: Ident) :: (app: Apply) :: _ =>
87+
contribute(
88+
Some(ident),
89+
app,
90+
indexedContext,
91+
clientSupportsSnippets,
92+
)
93+
case _ =>
94+
Nil
6995
case _ =>
7096
Nil
7197
end match
@@ -87,7 +113,7 @@ object NamedArgCompletions:
87113
ident: Option[Ident],
88114
apply: Apply,
89115
indexedContext: IndexedContext,
90-
clientSupportsSnippets: Boolean
116+
clientSupportsSnippets: Boolean,
91117
)(using context: Context): List[CompletionValue] =
92118
def isUselessLiteral(arg: Tree): Boolean =
93119
arg match
@@ -117,6 +143,11 @@ object NamedArgCompletions:
117143

118144
val argss = collectArgss(apply)
119145

146+
def fallbackFindApply(sym: Symbol) =
147+
sym.info.member(nme.apply) match
148+
case NoDenotation => Nil
149+
case den => List(den.symbol)
150+
120151
// fallback for when multiple overloaded methods match the supplied args
121152
def fallbackFindMatchingMethods() =
122153
def maybeNameAndIndexedContext(
@@ -182,7 +213,9 @@ object NamedArgCompletions:
182213
if foundPotential.contains(method.symbol) then foundPotential
183214
else method.symbol :: foundPotential
184215
else List(method.symbol)
185-
else fallbackFindMatchingMethods()
216+
else if method.symbol.is(Method) || method.symbol == NoSymbol then
217+
fallbackFindMatchingMethods()
218+
else fallbackFindApply(method.symbol)
186219
end if
187220
end matchingMethods
188221

@@ -227,8 +260,13 @@ object NamedArgCompletions:
227260
def refineParams(method: Tree, level: Int): List[ParamSymbol] =
228261
method match
229262
case Select(Apply(f, _), _) => refineParams(f, level + 1)
230-
case Select(h, v) => getRefinedParams(h.symbol.info, level)
231-
case _ => defaultBaseParams
263+
case Select(h, name) =>
264+
// for Select(foo, name = apply) we want `foo.symbol`
265+
if name == nme.apply then getRefinedParams(h.symbol.info, level)
266+
else getRefinedParams(method.symbol.info, level)
267+
case Apply(f, _) =>
268+
refineParams(f, level + 1)
269+
case _ => getRefinedParams(method.symbol.info, level)
232270
refineParams(method, 0)
233271
end baseParams
234272

@@ -330,15 +368,15 @@ object NamedArgCompletions:
330368
param.nameBackticked + " = " + memberName + " "
331369
CompletionValue.namedArg(
332370
label = editText,
333-
param
371+
param,
334372
)
335373
}
336374
}
337375

338376
params.map(p =>
339377
CompletionValue.namedArg(
340378
s"${p.nameBackticked} = ",
341-
p
379+
p,
342380
)
343381
) ::: findPossibleDefaults() ::: fillAllFields()
344382
end contribute
@@ -411,4 +449,4 @@ case class JustSymbol(symbol: Symbol)(using Context) extends ParamSymbol:
411449
def info: Type = symbol.info
412450

413451
case class RefinedSymbol(symbol: Symbol, name: Name, info: Type)
414-
extends ParamSymbol
452+
extends ParamSymbol

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,3 +1038,71 @@ class CompletionArgSuite extends BaseCompletionSuite:
10381038
|""".stripMargin,
10391039
topLines = Some(1),
10401040
)
1041+
1042+
@Test def `second-first` =
1043+
check(
1044+
"""|object Main {
1045+
| def foo(aaa: Int, bbb: Int, ccc: Int) = aaa + bbb + ccc
1046+
| val k = foo (
1047+
| bbb = 123,
1048+
| aa@@
1049+
| )
1050+
|}
1051+
|""".stripMargin,
1052+
"""|aaa = : Int
1053+
|""".stripMargin,
1054+
topLines = Some(1),
1055+
)
1056+
1057+
@Test def `second-first2` =
1058+
check(
1059+
"""|object Main {
1060+
| def foo(aaa: Int, bbb: Int, ccc: Int) = aaa + bbb + ccc
1061+
| val k = foo (
1062+
| bbb = 123,
1063+
| ccc = 123,
1064+
| aa@@
1065+
| )
1066+
|}
1067+
|""".stripMargin,
1068+
"""|aaa = : Int
1069+
|""".stripMargin,
1070+
topLines = Some(1),
1071+
)
1072+
1073+
@Test def `second-first3` =
1074+
check(
1075+
"""|object Main {
1076+
| def foo(ddd: Int)(aaa: Int, bbb: Int, ccc: Int) = aaa + bbb + ccc
1077+
| val k = foo(123)(
1078+
| bbb = 123,
1079+
| ccc = 123,
1080+
| aa@@
1081+
| )
1082+
|}
1083+
|""".stripMargin,
1084+
"""|aaa = : Int
1085+
|""".stripMargin,
1086+
topLines = Some(1),
1087+
)
1088+
1089+
@Test def `second-first4` =
1090+
check(
1091+
"""|object O:
1092+
| val hello: (x: Int, y: Int) => Unit = (x, _) => println(x)
1093+
|val k = O.hello(y = 1, @@)
1094+
|""".stripMargin,
1095+
"""|x = : Int
1096+
|""".stripMargin,
1097+
topLines = Some(1),
1098+
)
1099+
1100+
@Test def `second-first5` =
1101+
check(
1102+
"""|val hello: (x: Int) => Int => (str: String, ccc: String) => Unit = x => j => (str, _) => println(str)
1103+
|val k = hello(x = 1)(2)(ccc = "abc", @@)
1104+
|""".stripMargin,
1105+
"""|str = : String
1106+
| """.stripMargin,
1107+
topLines = Some(1),
1108+
)

0 commit comments

Comments
 (0)