From d2ccce22ad3cd1bb6befe400dd2d9ad53683b232 Mon Sep 17 00:00:00 2001 From: Jakub Ciesluk <323892@uwr.edu.pl> Date: Mon, 18 Sep 2023 17:50:26 +0200 Subject: [PATCH] bugfix: Named args completions with default values --- .../pc/completions/ArgCompletions.scala | 7 +- .../pc/completions/NamedArgCompletions.scala | 17 ++- .../scala/tests/pc/CompletionArgSuite.scala | 115 ++++++++++++++++++ 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/completions/ArgCompletions.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/completions/ArgCompletions.scala index 598b4aa9770..1cfcf6179eb 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/completions/ArgCompletions.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/completions/ArgCompletions.scala @@ -19,7 +19,12 @@ trait ArgCompletions { this: MetalsGlobal => val editRange: l.Range = pos.withStart(ident.pos.start).withEnd(pos.start).toLsp val funPos = apply.fun.pos - val method: Tree = typedTreeAt(funPos) + val method: Tree = typedTreeAt(funPos) match { + case Apply(Block(defParams, app: Apply), _) + if defParams.forall(p => p.isInstanceOf[ValDef]) => + app + case t => t + } val methodSym = method.symbol lazy val methodsParams: List[MethodParams] = { if (methodSym.isModule) getParamsFromObject(methodSym) diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/completions/NamedArgCompletions.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/completions/NamedArgCompletions.scala index 712bc5dac56..a18e7c3ffa1 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/completions/NamedArgCompletions.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/completions/NamedArgCompletions.scala @@ -237,15 +237,24 @@ object NamedArgCompletions: .getOrElse(baseArgs) .filterNot(isUselessLiteral) + @tailrec + def isDefaultArg(t: Tree): Boolean = t match + // default args + case Ident(name) => name.is(DefaultGetterName) + // default args for methods defined in object + case Select(_, name) => + name.is(DefaultGetterName) + // default args in not-first parameter list + // eg. def m(fst: Int)(snd: Int)(arg1: Int, arg2: Int = 123) = ??? + case Apply(fun, _) => isDefaultArg(fun) + case _ => false + val isNamed: Set[Name] = args.iterator .zip(baseParams.iterator) // filter out synthesized args and default arg getters .filterNot { case (arg, _) if arg.symbol.denot.is(Flags.Synthetic) => true - case (Ident(name), _) => name.is(DefaultGetterName) // default args - case (Select(Ident(_), name), _) => - name.is(DefaultGetterName) // default args for apply method - case _ => false + case (arg, _) => isDefaultArg(arg) } .map { case (NamedArg(name, _), _) => name diff --git a/tests/cross/src/test/scala/tests/pc/CompletionArgSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionArgSuite.scala index 70503cfa302..58c0018f9a1 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionArgSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionArgSuite.scala @@ -244,6 +244,121 @@ class CompletionArgSuite extends BaseCompletionSuite { "", ) + check( + "default-args", + """|object Main { + | def foo() = { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(@@) + | } + |} + |""".stripMargin, + """|fst = : String + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + check( + "default-args2", + """|object Main { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(@@) + |} + |""".stripMargin, + """|fst = : String + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + check( + "default-args3", + """|object Main { + | def deployment(str: String)( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment("str")( + | @@ + | ) + |} + |""".stripMargin, + """|fst = : String + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + check( + "default-args4", + """|object Main { + | def deployment(str: String)(opt: Option[Int])( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment("str")(None)( + | @@ + | ) + |} + |""".stripMargin, + """|fst = : String + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + // NOTE: In Scala 3.3.1 the tree for this test changed, which allowed easy fix + // For previous Scala 3 versions it shows wrong completions + check( + "default-args5", + """|object Main { + | def deployment(str: String)(opt: Option[Int] = None)( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment("str")( + | @@ + | ) + |} + |""".stripMargin, + """|abc: (String, Int) => Option[Int] + |""".stripMargin, + topLines = Some(1), + compat = Map( + ">=3.3.1" -> + """|opt = : Option[Int] + |""".stripMargin, + "2" -> + """|opt = : Option[Int] + |""".stripMargin, + ), + ) + + check( + "default-args6".tag(IgnoreScala2), + """|object Main { + | def deployment(using str: String)( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(using "str")( + | @@ + | ) + |} + |""".stripMargin, + """|fst = : String + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + checkSnippet( // see: https://github.com/scalameta/metals/issues/2400 "explicit-dollar", """