Skip to content

Commit 61a15b2

Browse files

File tree

9 files changed

+243
-21
lines changed

9 files changed

+243
-21
lines changed

presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala

+16-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import dotty.tools.dotc.ast.Trees.*
1313
import dotty.tools.dotc.ast.tpd
1414
import dotty.tools.dotc.ast.tpd.DeepFolder
1515
import dotty.tools.dotc.core.Contexts.*
16+
import dotty.tools.dotc.core.Flags
1617
import dotty.tools.dotc.core.Symbols.Symbol
1718
import dotty.tools.dotc.core.Types.MethodType
1819
import dotty.tools.dotc.core.Types.PolyType
@@ -116,9 +117,15 @@ final class ExtractMethodProvider(
116117
typeParams.toList.sortBy(_.decodedName),
117118
)
118119
end localRefs
120+
val optEnclosing =
121+
path.dropWhile(src => !src.sourcePos.encloses(range)) match
122+
case Nil => None
123+
case _ :: (app @ Apply(fun, args)) :: _ if args.exists(ImplicitParameters.isSyntheticArg(_)) => Some(app)
124+
case found :: _ => Some(found)
125+
119126
val edits =
120127
for
121-
enclosing <- path.find(src => src.sourcePos.encloses(range))
128+
enclosing <- optEnclosing
122129
extracted = extractFromBlock(enclosing)
123130
head <- extracted.headOption
124131
expr <- extracted.lastOption
@@ -131,11 +138,14 @@ final class ExtractMethodProvider(
131138
val exprType = prettyPrint(expr.typeOpt.widen)
132139
val name =
133140
genName(indexedCtx.scopeSymbols.map(_.decodedName).toSet, "newMethod")
134-
val (methodParams, typeParams) =
141+
val (allMethodParams, typeParams) =
135142
localRefs(extracted, stat.sourcePos, extractedPos)
136-
val methodParamsText = methodParams
137-
.map(sym => s"${sym.decodedName}: ${prettyPrint(sym.info)}")
138-
.mkString(", ")
143+
val (methodParams, implicitParams) = allMethodParams.partition(!_.isOneOf(Flags.GivenOrImplicit))
144+
def toParamText(params: List[Symbol]) =
145+
params.map(sym => s"${sym.decodedName}: ${prettyPrint(sym.info)}")
146+
.mkString(", ")
147+
val methodParamsText = toParamText(methodParams)
148+
val implicitParamsText = if implicitParams.nonEmpty then s"(given ${toParamText(implicitParams)})" else ""
139149
val typeParamsText = typeParams
140150
.map(_.decodedName) match
141151
case Nil => ""
@@ -155,7 +165,7 @@ final class ExtractMethodProvider(
155165
if noIndent && extracted.length > 1 then (" {", s"$newIndent}")
156166
else ("", "")
157167
val defText =
158-
s"def $name$typeParamsText($methodParamsText): $exprType =$obracket\n${toExtract}\n$cbracket\n$newIndent"
168+
s"def $name$typeParamsText($methodParamsText)$implicitParamsText: $exprType =$obracket\n${toExtract}\n$cbracket\n$newIndent"
159169
val replacedText = s"$name($exprParamsText)"
160170
List(
161171
new l.TextEdit(

presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ sealed trait IndexedContext:
3636
Result.InScope
3737
// when all the conflicting symbols came from an old version of the file
3838
case Some(symbols) if symbols.nonEmpty && symbols.forall(_.isStale) => Result.Missing
39-
case Some(_) => Result.Conflict
40-
case None => Result.Missing
39+
case Some(symbols) if symbols.exists(rename(_).isEmpty) => Result.Conflict
40+
case _ => Result.Missing
4141
end lookupSym
4242

4343
/**

presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package dotty.tools.pc
33

44
import java.nio.file.Paths
55

6+
import scala.annotation.tailrec
7+
68
import scala.meta.internal.metals.ReportContext
79
import dotty.tools.pc.utils.InteractiveEnrichments.*
810
import dotty.tools.pc.printer.ShortenedTypePrinter
@@ -194,10 +196,10 @@ object ImplicitConversion:
194196
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) =
195197
if (params.implicitConversions()) {
196198
tree match
197-
case Apply(fun: Ident, args) if isSynthetic(fun) =>
199+
case Apply(fun: Ident, args) if isSynthetic(fun) && args.exists(!_.span.isZeroExtent) =>
198200
implicitConversion(fun, args)
199201
case Apply(Select(fun, name), args)
200-
if name == nme.apply && isSynthetic(fun) =>
202+
if name == nme.apply && isSynthetic(fun) && args.exists(!_.span.isZeroExtent) =>
201203
implicitConversion(fun, args)
202204
case _ => None
203205
} else None
@@ -218,7 +220,7 @@ object ImplicitParameters:
218220
if (params.implicitParameters()) {
219221
tree match
220222
case Apply(fun, args)
221-
if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent =>
223+
if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent && !args.exists(isQuotes(_)) =>
222224
val (implicitArgs, providedArgs) = args.partition(isSyntheticArg)
223225
val allImplicit = providedArgs.isEmpty || providedArgs.forall {
224226
case Ident(name) => name == nme.MISSING
@@ -229,10 +231,12 @@ object ImplicitParameters:
229231
case _ => None
230232
} else None
231233

232-
private def isSyntheticArg(tree: Tree)(using Context) = tree match
234+
@tailrec
235+
def isSyntheticArg(tree: Tree)(using Context): Boolean = tree match
233236
case tree: Ident =>
234-
tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) &&
235-
!isQuotes(tree)
237+
tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit)
238+
case Apply(fun, _ ) if tree.span.isZeroExtent => isSyntheticArg(fun)
239+
case TypeApply(fun, _ ) if tree.span.isZeroExtent => isSyntheticArg(fun)
236240
case _ => false
237241

238242
// Decorations for Quotes are rarely useful

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,14 @@ object OverrideCompletions:
279279
else ""
280280
(indent, indent, lastIndent)
281281
end calcIndent
282-
val abstractMembers = defn.typeOpt.abstractTermMembers.map(_.symbol)
282+
val abstractMembers =
283+
defn.tpe.abstractTermMembers.map(_.symbol).groupBy(_.owner).map {
284+
case (owner, members) => (owner, members.sortWith{ (sym1, sym2) =>
285+
if(sym1.sourcePos.exists && sym2.sourcePos.exists)
286+
sym1.sourcePos.start <= sym2.sourcePos.start
287+
else !sym2.sourcePos.exists
288+
})
289+
}.toSeq.sortBy(_._1.name.decoded).flatMap(_._2)
283290

284291
val caseClassOwners = Set("Product", "Equals")
285292
val overridables =
@@ -506,6 +513,8 @@ object OverrideCompletions:
506513
defn match
507514
case td: TypeDef if text.charAt(td.rhs.span.end) == ':' =>
508515
Some(td.rhs.span.end)
516+
case TypeDef(_, temp : Template) =>
517+
temp.parentsOrDerived.lastOption.map(_.span.end).filter(text.charAt(_) == ':')
509518
case _ => None
510519

511520
private def fallbackFromParent(parent: Tree, name: String)(using Context) =

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

+31
Original file line numberDiff line numberDiff line change
@@ -1952,3 +1952,34 @@ class CompletionSuite extends BaseCompletionSuite:
19521952
"""TestEnum test
19531953
|""".stripMargin,
19541954
)
1955+
1956+
@Test def `i6477-1` =
1957+
checkEdit(
1958+
"""|package a
1959+
|import a.b.SomeClass as SC
1960+
|
1961+
|package b {
1962+
| class SomeClass
1963+
|}
1964+
|package c {
1965+
| class SomeClass
1966+
|}
1967+
|
1968+
|val bar: SC = ???
1969+
|val foo: SomeClass@@
1970+
|""".stripMargin,
1971+
"""|package a
1972+
|import a.b.SomeClass as SC
1973+
|import a.c.SomeClass
1974+
|
1975+
|package b {
1976+
| class SomeClass
1977+
|}
1978+
|package c {
1979+
| class SomeClass
1980+
|}
1981+
|
1982+
|val bar: SC = ???
1983+
|val foo: SomeClass
1984+
|""".stripMargin,
1985+
)

presentation-compiler/test/dotty/tools/pc/tests/edit/AutoImplementAbstractMembersSuite.scala

+31-6
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ class AutoImplementAbstractMembersSuite extends BaseCodeActionSuite:
345345
|object Main {
346346
| class Baz extends Bar {
347347
|
348-
| override def foo: Int = ???
349-
|
350348
| override def bar: Int = ???
351349
|
350+
| override def foo: Int = ???
351+
|
352352
| }
353353
|}
354354
|""".stripMargin
@@ -1243,7 +1243,6 @@ class AutoImplementAbstractMembersSuite extends BaseCodeActionSuite:
12431243
|
12441244
|object A {
12451245
| trait Base:
1246-
| def foo(x: Int): Int
12471246
| def bar(x: String): String
12481247
|
12491248
| class <<Concrete>>(x: Int, y: String) extends Base:
@@ -1256,13 +1255,10 @@ class AutoImplementAbstractMembersSuite extends BaseCodeActionSuite:
12561255
|
12571256
|object A {
12581257
| trait Base:
1259-
| def foo(x: Int): Int
12601258
| def bar(x: String): String
12611259
|
12621260
| class Concrete(x: Int, y: String) extends Base:
12631261
|
1264-
| override def foo(x: Int): Int = ???
1265-
|
12661262
| override def bar(x: String): String = ???
12671263
|
12681264
|
@@ -1272,6 +1268,35 @@ class AutoImplementAbstractMembersSuite extends BaseCodeActionSuite:
12721268
|""".stripMargin,
12731269
)
12741270

1271+
@Test def `braceless-case-class` =
1272+
checkEdit(
1273+
"""|package a
1274+
|
1275+
|trait Base:
1276+
| def foo(x: Int): Int
1277+
| def bar(x: String): String
1278+
|
1279+
|case class <<Concrete>>() extends Base:
1280+
| def aaa = "aaa"
1281+
|end Concrete
1282+
|""".stripMargin,
1283+
"""|package a
1284+
|
1285+
|trait Base:
1286+
| def foo(x: Int): Int
1287+
| def bar(x: String): String
1288+
|
1289+
|case class Concrete() extends Base:
1290+
|
1291+
| override def foo(x: Int): Int = ???
1292+
|
1293+
| override def bar(x: String): String = ???
1294+
|
1295+
| def aaa = "aaa"
1296+
|end Concrete
1297+
|""".stripMargin
1298+
)
1299+
12751300
def checkEdit(
12761301
original: String,
12771302
expected: String

presentation-compiler/test/dotty/tools/pc/tests/edit/AutoImportsSuite.scala

+31
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,37 @@ class AutoImportsSuite extends BaseAutoImportsSuite:
405405
|""".stripMargin,
406406
)
407407

408+
@Test def `i6477` =
409+
checkEdit(
410+
"""|package a
411+
|import a.b.SomeClass as SC
412+
|
413+
|package b {
414+
| class SomeClass
415+
|}
416+
|package c {
417+
| class SomeClass
418+
|}
419+
|
420+
|val bar: SC = ???
421+
|val foo: <<SomeClass>> = ???
422+
|""".stripMargin,
423+
"""|package a
424+
|import a.b.SomeClass as SC
425+
|import a.c.SomeClass
426+
|
427+
|package b {
428+
| class SomeClass
429+
|}
430+
|package c {
431+
| class SomeClass
432+
|}
433+
|
434+
|val bar: SC = ???
435+
|val foo: SomeClass = ???
436+
|""".stripMargin
437+
)
438+
408439
private def ammoniteWrapper(code: String): String =
409440
// Vaguely looks like a scala file that Ammonite generates
410441
// from a sc file.

presentation-compiler/test/dotty/tools/pc/tests/edit/ExtractMethodSuite.scala

+92
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,95 @@ class ExtractMethodSuite extends BaseExtractMethodSuite:
446446
| }
447447
|}""".stripMargin
448448
)
449+
450+
@Test def `i6476` =
451+
checkEdit(
452+
"""|object O {
453+
| class C
454+
| def foo(i: Int)(implicit o: C) = i
455+
|
456+
| @@val o = {
457+
| implicit val c = new C
458+
| <<foo(2)>>
459+
| ???
460+
| }
461+
|}
462+
|""".stripMargin,
463+
"""|object O {
464+
| class C
465+
| def foo(i: Int)(implicit o: C) = i
466+
|
467+
| def newMethod()(given c: C): Int =
468+
| foo(2)
469+
|
470+
| val o = {
471+
| implicit val c = new C
472+
| newMethod()
473+
| ???
474+
| }
475+
|}
476+
|""".stripMargin
477+
)
478+
479+
480+
@Test def `i6476-2` =
481+
checkEdit(
482+
"""|object O {
483+
| class C
484+
| def foo(i: Int)(implicit o: C) = i
485+
|
486+
| @@val o = {
487+
| <<foo(2)(new C)>>
488+
| ???
489+
| }
490+
|}
491+
|""".stripMargin,
492+
"""|object O {
493+
| class C
494+
| def foo(i: Int)(implicit o: C) = i
495+
|
496+
| def newMethod(): Int =
497+
| foo(2)(new C)
498+
|
499+
| val o = {
500+
| newMethod()
501+
| ???
502+
| }
503+
|}
504+
|""".stripMargin
505+
)
506+
507+
@Test def `i6476-3` =
508+
checkEdit(
509+
"""|object O {
510+
| class C
511+
| class D
512+
| def foo(i: Int)(using o: C)(x: Int)(using d: D) = i
513+
|
514+
| @@val o = {
515+
| given C = new C
516+
| given D = new D
517+
| val w = 2
518+
| <<foo(w)(w)>>
519+
| ???
520+
| }
521+
|}
522+
|""".stripMargin,
523+
"""|object O {
524+
| class C
525+
| class D
526+
| def foo(i: Int)(using o: C)(x: Int)(using d: D) = i
527+
|
528+
| def newMethod(w: Int)(given given_C: C, given_D: D): Int =
529+
| foo(w)(w)
530+
|
531+
| val o = {
532+
| given C = new C
533+
| given D = new D
534+
| val w = 2
535+
| newMethod(w)
536+
| ???
537+
| }
538+
|}
539+
|""".stripMargin
540+
)

0 commit comments

Comments
 (0)