Skip to content

Commit 06b8909

Browse files
authored
Merge pull request #438 from scala/backport-lts-3.3-23283
Backport "Add inlay hints for by-name parameters" to 3.3 LTS
2 parents 1956e05 + e675dc7 commit 06b8909

File tree

2 files changed

+162
-5
lines changed

2 files changed

+162
-5
lines changed

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,29 @@ class PcInlayHintsProvider(
116116
InlayHintKind.Type,
117117
)
118118
.addDefinition(adjustedPos.start)
119+
case ByNameParameters(byNameParams) =>
120+
def adjustByNameParameterPos(pos: SourcePosition): SourcePosition =
121+
val adjusted = adjustPos(pos)
122+
val start = text.indexWhere(!_.isWhitespace, adjusted.start)
123+
val end = text.lastIndexWhere(!_.isWhitespace, adjusted.end - 1)
124+
125+
val startsWithBrace = text.lift(start).contains('{')
126+
val endsWithBrace = text.lift(end).contains('}')
127+
128+
if startsWithBrace && endsWithBrace then
129+
adjusted.withStart(start + 1)
130+
else
131+
adjusted
132+
133+
byNameParams.foldLeft(inlayHints) {
134+
case (ih, pos) =>
135+
val adjusted = adjustByNameParameterPos(pos)
136+
ih.add(
137+
adjusted.startPos.toLsp,
138+
List(LabelPart("=> ")),
139+
InlayHintKind.Parameter
140+
)
141+
}
119142
case _ => inlayHints
120143

121144
private def toLabelParts(
@@ -388,3 +411,28 @@ object InferredType:
388411
index >= 0 && index < afterDef.size && afterDef(index) == '@'
389412

390413
end InferredType
414+
415+
object ByNameParameters:
416+
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context): Option[List[SourcePosition]] =
417+
def shouldSkipSelect(sel: Select) =
418+
isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply
419+
420+
if (params.byNameParameters()){
421+
tree match
422+
case Apply(TypeApply(sel: Select, _), _) if shouldSkipSelect(sel) =>
423+
None
424+
case Apply(sel: Select, _) if shouldSkipSelect(sel) =>
425+
None
426+
case Apply(fun, args) =>
427+
val funTp = fun.typeOpt.widenTermRefExpr
428+
val params = funTp.paramInfoss.flatten
429+
Some(
430+
args
431+
.zip(params)
432+
.collect {
433+
case (tree, param) if param.isByName => tree.sourcePos
434+
}
435+
)
436+
case _ => None
437+
} else None
438+
end ByNameParameters

presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -611,17 +611,17 @@ class InlayHintsSuite extends BaseInlayHintsSuite {
611611
|class DemoSpec {
612612
| import ScalatestMock._
613613
|
614-
| /*StringTestOps<<(6:17)>>(*/"foo"/*)*/ should {
615-
| /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in {
614+
| /*StringTestOps<<(6:17)>>(*/"foo"/*)*/ should {/*=> */
615+
| /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in {/*=> */
616616
| checkThing1[String]/*(using instancesString<<(10:15)>>)*/
617617
| }/*(using here<<(5:15)>>)*/
618-
| /*StringTestOps<<(6:17)>>(*/"checkThing2"/*)*/ in {
618+
| /*StringTestOps<<(6:17)>>(*/"checkThing2"/*)*/ in {/*=> */
619619
| checkThing2[String]/*(using instancesString<<(10:15)>>, instancesString<<(10:15)>>)*/
620620
| }/*(using here<<(5:15)>>)*/
621621
| }/*(using subjectRegistrationFunction<<(3:15)>>)*/
622622
|
623-
| /*StringTestOps<<(6:17)>>(*/"bar"/*)*/ should {
624-
| /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in {
623+
| /*StringTestOps<<(6:17)>>(*/"bar"/*)*/ should {/*=> */
624+
| /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in {/*=> */
625625
| checkThing1[String]/*(using instancesString<<(10:15)>>)*/
626626
| }/*(using here<<(5:15)>>)*/
627627
| }/*(using subjectRegistrationFunction<<(3:15)>>)*/
@@ -1055,4 +1055,113 @@ class InlayHintsSuite extends BaseInlayHintsSuite {
10551055
|}
10561056
|""".stripMargin
10571057
)
1058+
1059+
@Test def `by-name-regular` =
1060+
check(
1061+
"""|object Main:
1062+
| def foo(x: => Int, y: Int, z: => Int)(w: Int, v: => Int): Unit = ()
1063+
| foo(1, 2, 3)(4, 5)
1064+
|""".stripMargin,
1065+
"""|object Main:
1066+
| def foo(x: => Int, y: Int, z: => Int)(w: Int, v: => Int): Unit = ()
1067+
| foo(/*=> */1, 2, /*=> */3)(4, /*=> */5)
1068+
|""".stripMargin
1069+
)
1070+
1071+
@Test def `by-name-block` =
1072+
check(
1073+
"""|object Main:
1074+
| def Future[A](arg: => A): A = arg
1075+
|
1076+
| Future(1 + 2)
1077+
| Future {
1078+
| 1 + 2
1079+
| }
1080+
| Future {
1081+
| val x = 1
1082+
| val y = 2
1083+
| x + y
1084+
| }
1085+
| Some(Option(2)
1086+
| .getOrElse {
1087+
| List(1,2)
1088+
| .headOption
1089+
| })
1090+
|""".stripMargin,
1091+
"""|object Main:
1092+
| def Future[A](arg: => A): A = arg
1093+
|
1094+
| Future/*[Int<<scala/Int#>>]*/(/*=> */1 + 2)
1095+
| Future/*[Int<<scala/Int#>>]*/ {/*=> */
1096+
| 1 + 2
1097+
| }
1098+
| Future/*[Int<<scala/Int#>>]*/ {/*=> */
1099+
| val x/*: Int<<scala/Int#>>*/ = 1
1100+
| val y/*: Int<<scala/Int#>>*/ = 2
1101+
| x + y
1102+
| }
1103+
| Some/*[Int<<scala/Int#>> | Option<<scala/Option#>>[Int<<scala/Int#>>]]*/(Option/*[Int<<scala/Int#>>]*/(2)
1104+
| .getOrElse/*[Int<<scala/Int#>> | Option<<scala/Option#>>[Int<<scala/Int#>>]]*/ {/*=> */
1105+
| List/*[Int<<scala/Int#>>]*/(1,2)
1106+
| .headOption
1107+
| })
1108+
|""".stripMargin
1109+
)
1110+
1111+
@Test def `by-name-for-comprehension` =
1112+
check(
1113+
"""|object Main:
1114+
| case class Test[A](v: A):
1115+
| def flatMap(f: => (A => Test[Int])): Test[Int] = f(v)
1116+
| def map(f: => (A => Int)): Test[Int] = Test(f(v))
1117+
|
1118+
| def main(args: Array[String]): Unit =
1119+
| val result: Test[Int] = for {
1120+
| a <- Test(10)
1121+
| b <- Test(20)
1122+
| } yield a + b
1123+
|
1124+
|""".stripMargin,
1125+
"""|object Main:
1126+
| case class Test[A](v: A):
1127+
| def flatMap(f: => (A => Test[Int])): Test[Int] = f(v)
1128+
| def map(f: => (A => Int)): Test[Int] = Test/*[Int<<scala/Int#>>]*/(f(v))
1129+
|
1130+
| def main(args: Array[String]): Unit =
1131+
| val result: Test[Int] = for {
1132+
| a <- Test/*[Int<<scala/Int#>>]*/(10)
1133+
| b <- Test/*[Int<<scala/Int#>>]*/(20)
1134+
| } yield a + b
1135+
|
1136+
|""".stripMargin,
1137+
)
1138+
1139+
@Test def `by-name-for-comprehension-generic` =
1140+
check(
1141+
"""|object Main:
1142+
| case class Test[A](v: A):
1143+
| def flatMap[B](f: => (A => Test[B])): Test[B] = f(v)
1144+
| def map[B](f: => (A => B)): Test[B] = Test(f(v))
1145+
|
1146+
| def main(args: Array[String]): Unit =
1147+
| val result: Test[Int] = for {
1148+
| a <- Test(10)
1149+
| b <- Test(20)
1150+
| } yield a + b
1151+
|
1152+
|""".stripMargin,
1153+
"""|object Main:
1154+
| case class Test[A](v: A):
1155+
| def flatMap[B](f: => (A => Test[B])): Test[B] = f(v)
1156+
| def map[B](f: => (A => B)): Test[B] = Test/*[B<<(4:13)>>]*/(f(v))
1157+
|
1158+
| def main(args: Array[String]): Unit =
1159+
| val result: Test[Int] = for {
1160+
| a <- Test/*[Int<<scala/Int#>>]*/(10)
1161+
| b <- Test/*[Int<<scala/Int#>>]*/(20)
1162+
| } yield a + b
1163+
|
1164+
|""".stripMargin,
1165+
)
1166+
10581167
}

0 commit comments

Comments
 (0)