Skip to content

Commit 91a1ea3

Browse files
mbovelajafri2001
andcommitted
Fix insertion of using in applications with trailing lambda syntax
Co-Authored-By: Abdullah Arif Jafri <[email protected]>
1 parent 0cd43fe commit 91a1ea3

File tree

16 files changed

+213
-25
lines changed

16 files changed

+213
-25
lines changed

Diff for: compiler/src/dotty/tools/dotc/ast/Trees.scala

+22
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,21 @@ object Trees {
513513
case Using // r.f(using x)
514514
case InfixTuple // r f (x1, ..., xN) where N != 1; needs to be treated specially for an error message in typedApply
515515

516+
/** The syntax used to pass the arguments in an application. */
517+
enum ApplyStyle:
518+
/** The arguments are passed in parentheses, e.g. `f(x)`. */
519+
case Parentheses
520+
/** A single argument is passed as a trailing lambda with braces, e.g.
521+
* `f { x => ... }`.
522+
*/
523+
case TrailingBraces
524+
/** A single argument is passed as a trailing lambda with colon and
525+
* indentation, e.g. `f: x => ...`.
526+
*/
527+
case TrailingColon
528+
/** The syntax used to pass the arguments is unknown. */
529+
case Unknown
530+
516531
/** fun(args) */
517532
case class Apply[+T <: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
518533
extends GenericApply[T] {
@@ -527,6 +542,13 @@ object Trees {
527542
*/
528543
def applyKind: ApplyKind =
529544
attachmentOrElse(untpd.KindOfApply, ApplyKind.Regular)
545+
546+
def setApplyStyle(style: ApplyStyle) =
547+
putAttachment(untpd.StyleOfApply, style)
548+
this
549+
550+
def applyStyle: ApplyStyle =
551+
attachmentOrElse(untpd.StyleOfApply, ApplyStyle.Unknown)
530552
}
531553

532554
/** fun[args] */

Diff for: compiler/src/dotty/tools/dotc/ast/tpd.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1662,5 +1662,5 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
16621662

16631663

16641664
protected def FunProto(args: List[Tree], resType: Type)(using Context) =
1665-
ProtoTypes.FunProtoTyped(args, resType)(ctx.typer, ApplyKind.Regular)
1665+
ProtoTypes.FunProtoTyped(args, resType)(ctx.typer, ApplyKind.Regular, ApplyStyle.Unknown)
16661666
}

Diff for: compiler/src/dotty/tools/dotc/ast/untpd.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
386386
/** Property key for contextual Apply trees of the form `fn given arg` */
387387
val KindOfApply: Property.StickyKey[ApplyKind] = Property.StickyKey()
388388

389+
/** Property key to attach an [[ApplyStyle]] to [[Apply]] trees. */
390+
val StyleOfApply: Property.StickyKey[ApplyStyle] = Property.StickyKey()
391+
389392
// ------ Creation methods for untyped only -----------------
390393

391394
def Ident(name: Name)(implicit src: SourceFile): Ident = new Ident(name)
@@ -859,5 +862,5 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
859862
}
860863

861864
protected def FunProto(args: List[Tree], resType: Type)(using Context) =
862-
ProtoTypes.FunProto(args, resType)(ctx.typer, ApplyKind.Regular)
865+
ProtoTypes.FunProto(args, resType)(ctx.typer, ApplyKind.Regular, ApplyStyle.Unknown)
863866
}

Diff for: compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+26-6
Original file line numberDiff line numberDiff line change
@@ -2805,7 +2805,8 @@ object Parsers {
28052805
val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) }
28062806
simpleExprRest(tapp, location, canApply = true)
28072807
case LPAREN | LBRACE | INDENT if canApply =>
2808-
val app = atSpan(startOffset(t), in.offset) { mkApply(t, argumentExprs()) }
2808+
val beforeArgs = in.token
2809+
val app = atSpan(startOffset(t), in.offset) { mkApply(t, argumentExprs(), beforeArgs) }
28092810
if in.rewriteToIndent then
28102811
app match
28112812
case Apply(Apply(_, List(Block(_, _))), List(blk @ Block(_, _))) =>
@@ -2827,8 +2828,9 @@ object Parsers {
28272828
}
28282829
case _ => t
28292830
else if isColonLambda then
2831+
val beforeArgs = in.token
28302832
val app = atSpan(startOffset(t), in.skipToken()) {
2831-
Apply(t, expr(Location.InColonArg) :: Nil)
2833+
mkApply(t, (expr(Location.InColonArg) :: Nil, false), beforeArgs)
28322834
}
28332835
simpleExprRest(app, location, canApply = true)
28342836
else t
@@ -2892,8 +2894,22 @@ object Parsers {
28922894
def argumentExprs(): (List[Tree], Boolean) =
28932895
if (in.isNestedStart) (blockExpr() :: Nil, false) else parArgumentExprs()
28942896

2895-
def mkApply(fn: Tree, args: (List[Tree], Boolean)): Tree =
2897+
/** Creates an [[Apply]] tree.
2898+
*
2899+
* @param fn The function to apply.
2900+
* @param args A pair containing the list of arguments and a boolean
2901+
* indicating if the list is preceded by `using`.
2902+
* @param beforeArgs The token that precedes the arguments.
2903+
*/
2904+
def mkApply(fn: Tree, args: (List[Tree], Boolean), beforeArgs: Token): Tree =
28962905
val res = Apply(fn, args._1)
2906+
val applyStyle =
2907+
beforeArgs match
2908+
case LPAREN => ApplyStyle.Parentheses
2909+
case LBRACE => ApplyStyle.TrailingBraces
2910+
case INDENT | COLONfollow | COLONeol => ApplyStyle.TrailingColon
2911+
case _ => ApplyStyle.Unknown
2912+
res.setApplyStyle(applyStyle)
28972913
if args._2 then res.setApplyKind(ApplyKind.Using)
28982914
res
28992915

@@ -2905,7 +2921,9 @@ object Parsers {
29052921
*/
29062922
def argumentExprss(fn: Tree): Tree = {
29072923
argumentStart()
2908-
if (in.token == LPAREN || in.isNestedStart) argumentExprss(mkApply(fn, argumentExprs()))
2924+
if in.token == LPAREN || in.isNestedStart then
2925+
val beforeArgs = in.token
2926+
argumentExprss(mkApply(fn, argumentExprs(), beforeArgs))
29092927
else fn
29102928
}
29112929

@@ -2930,8 +2948,9 @@ object Parsers {
29302948
}
29312949
}
29322950
if (in.token == LPAREN && (!inClassConstrAnnots || isLegalAnnotArg))
2951+
val beforeArgs = in.token
29332952
parArgumentExprss(
2934-
atSpan(startOffset(fn)) { mkApply(fn, parArgumentExprs()) }
2953+
atSpan(startOffset(fn)) { mkApply(fn, parArgumentExprs(), beforeArgs) }
29352954
)
29362955
else fn
29372956
}
@@ -4046,7 +4065,8 @@ object Parsers {
40464065
def selfInvocation(): Tree =
40474066
atSpan(accept(THIS)) {
40484067
argumentStart()
4049-
argumentExprss(mkApply(Ident(nme.CONSTRUCTOR), argumentExprs()))
4068+
val beforeArgs = in.token
4069+
argumentExprss(mkApply(Ident(nme.CONSTRUCTOR), argumentExprs(), beforeArgs))
40504070
}
40514071

40524072
/** TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds [‘=’ Type]

Diff for: compiler/src/dotty/tools/dotc/typer/Applications.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ trait Applications extends Compatibility {
10851085
// Do ignore other expected result types, since there might be an implicit conversion
10861086
// on the result. We could drop this if we disallow unrestricted implicit conversions.
10871087
val originalProto =
1088-
new FunProto(tree.args, resultProto)(this, tree.applyKind)(using argCtx(tree))
1088+
new FunProto(tree.args, resultProto)(this, tree.applyKind, tree.applyStyle)(using argCtx(tree))
10891089
record("typedApply")
10901090
val fun1 = typedExpr(tree.fun, originalProto)
10911091

Diff for: compiler/src/dotty/tools/dotc/typer/Migrations.scala

+9-3
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,18 @@ trait Migrations:
133133
val rewriteMsg = Message.rewriteNotice("This code", mversion.patchFrom)
134134
report.errorOrMigrationWarning(
135135
em"""Implicit parameters should be provided with a `using` clause.$rewriteMsg
136-
|To disable the warning, please use the following option:
136+
|To disable the warning, please use the following option:
137137
| "-Wconf:msg=Implicit parameters should be provided with a `using` clause:s"
138-
|""",
138+
|""",
139139
pt.args.head.srcPos, mversion)
140140
if mversion.needsPatch then
141-
patch(Span(pt.args.head.span.start), "using ")
141+
// In order to insert a `using`, the application needs to be done with
142+
// parentheses syntax. See issue #22927 and related tests.
143+
patch(Span(tree.span.end, pt.args.head.span.start), "(using ")
144+
if pt.applyStyle != ApplyStyle.Parentheses then
145+
// If the application wasn't done with the parentheses syntax, we need
146+
// to add a trailing closing parenthesis.
147+
patch(Span(pt.args.head.span.end), ")")
142148
end implicitParams
143149

144150
end Migrations

Diff for: compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

+14-8
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ object ProtoTypes {
344344
trait ApplyingProto extends ProtoType // common trait of ViewProto and FunProto
345345
trait FunOrPolyProto extends ProtoType: // common trait of PolyProto and FunProto
346346
def applyKind: ApplyKind = ApplyKind.Regular
347+
def applyStyle: ApplyStyle = ApplyStyle.Unknown
347348

348349
class FunProtoState {
349350

@@ -368,9 +369,10 @@ object ProtoTypes {
368369
* [](args): resultType
369370
*
370371
* @param args The untyped arguments to which the function is applied
371-
* @param resType The expeected result type
372+
* @param resType The expected result type
372373
* @param typer The typer to use for typing the arguments
373374
* @param applyKind The kind of application (regular/using/tupled infix operand)
375+
* @param applyStyle The [[ApplyStyle]] of the application
374376
* @param state The state object to use for tracking the changes to this prototype
375377
* @param constrainResultDeep
376378
* A flag to indicate that constrainResult on this prototype
@@ -379,6 +381,7 @@ object ProtoTypes {
379381
case class FunProto(args: List[untpd.Tree], resType: Type)(
380382
typer: Typer,
381383
override val applyKind: ApplyKind,
384+
override val applyStyle: ApplyStyle,
382385
state: FunProtoState = new FunProtoState,
383386
val constrainResultDeep: Boolean = false)(using protoCtx: Context)
384387
extends UncachedGroundType with ApplyingProto with FunOrPolyProto {
@@ -402,7 +405,7 @@ object ProtoTypes {
402405
&& (typer eq this.typer)
403406
&& constrainResultDeep == this.constrainResultDeep
404407
then this
405-
else new FunProto(args, resultType)(typer, applyKind, constrainResultDeep = constrainResultDeep)
408+
else new FunProto(args, resultType)(typer, applyKind, applyStyle, constrainResultDeep = constrainResultDeep)
406409

407410
/** @return True if all arguments have types.
408411
*/
@@ -572,7 +575,7 @@ object ProtoTypes {
572575
val dualArgs = args match
573576
case untpd.Tuple(elems) :: Nil => elems
574577
case _ => untpd.Tuple(args) :: Nil
575-
state.tupledDual = new FunProto(dualArgs, resultType)(typer, applyKind)
578+
state.tupledDual = new FunProto(dualArgs, resultType)(typer, applyKind, applyStyle)
576579
tupledDual
577580
}
578581

@@ -614,15 +617,15 @@ object ProtoTypes {
614617

615618
override def withContext(newCtx: Context): ProtoType =
616619
if newCtx `eq` protoCtx then this
617-
else new FunProto(args, resType)(typer, applyKind, state)(using newCtx)
620+
else new FunProto(args, resType)(typer, applyKind, applyStyle, state)(using newCtx)
618621
}
619622

620623
/** A prototype for expressions that appear in function position
621624
*
622625
* [](args): resultType, where args are known to be typed
623626
*/
624-
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, applyKind: ApplyKind)(using Context)
625-
extends FunProto(args, resultType)(typer, applyKind):
627+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, applyKind: ApplyKind, applyStyle: ApplyStyle)(using Context)
628+
extends FunProto(args, resultType)(typer, applyKind, applyStyle):
626629
override def typedArgs(norm: (untpd.Tree, Int) => untpd.Tree)(using Context): List[tpd.Tree] = args
627630
override def typedArg(arg: untpd.Tree, formal: Type)(using Context): tpd.Tree = arg.asInstanceOf[tpd.Tree]
628631
override def allArgTypesAreCurrent()(using Context): Boolean = true
@@ -684,7 +687,10 @@ object ProtoTypes {
684687
}
685688

686689
class UnapplyFunProto(argType: Type, typer: Typer)(using Context) extends FunProto(
687-
untpd.TypedSplice(dummyTreeOfType(argType)(ctx.source)) :: Nil, WildcardType)(typer, applyKind = ApplyKind.Regular)
690+
untpd.TypedSplice(dummyTreeOfType(argType)(ctx.source)) :: Nil, WildcardType
691+
)(
692+
typer, applyKind = ApplyKind.Regular, applyStyle = ApplyStyle.Parentheses
693+
)
688694

689695
/** A prototype for expressions [] that are type-parameterized:
690696
*
@@ -1006,7 +1012,7 @@ object ProtoTypes {
10061012
if (args eq tp.args) && (resTp eq tp.resultType) then
10071013
tp
10081014
else
1009-
FunProtoTyped(args, resTp)(ctx.typer, tp.applyKind)
1015+
FunProtoTyped(args, resTp)(ctx.typer, tp.applyKind, tp.applyStyle)
10101016
case tp: IgnoredProto =>
10111017
WildcardType
10121018
case _: ThisType | _: BoundType => // default case, inlined for speed

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1861,7 +1861,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
18611861
val nestedCtx = outerCtx.fresh.setNewTyperState()
18621862
inContext(nestedCtx) {
18631863
val protoArgs = args map (_ withType WildcardType)
1864-
val callProto = FunProto(protoArgs, WildcardType)(this, app.applyKind)
1864+
val callProto = FunProto(protoArgs, WildcardType)(this, app.applyKind, app.applyStyle)
18651865
val expr1 = typedExpr(expr, callProto)
18661866
if nestedCtx.reporter.hasErrors then NoType
18671867
else inContext(outerCtx) {

Diff for: compiler/test/dotty/tools/dotc/CompilationTests.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ class CompilationTests {
8888
compileFile("tests/rewrites/ambiguous-named-tuple-assignment.scala", defaultOptions.and("-rewrite", "-source:3.6-migration")),
8989
compileFile("tests/rewrites/i21382.scala", defaultOptions.and("-indent", "-rewrite")),
9090
compileFile("tests/rewrites/unused.scala", defaultOptions.and("-rewrite", "-Wunused:all")),
91-
compileFile("tests/rewrites/i22440.scala", defaultOptions.and("-rewrite"))
91+
compileFile("tests/rewrites/i22440.scala", defaultOptions.and("-rewrite")),
92+
compileFile("tests/rewrites/i22927.scala", defaultOptions.and("-rewrite", "-source:3.7-migration")),
93+
compileFile("tests/rewrites/i22927b.scala", defaultOptions.and("-rewrite", "-source:3.7-migration"))
9294
).checkRewrites()
9395
}
9496

Diff for: tests/neg/i22440.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
| ^
44
| Implicit parameters should be provided with a `using` clause.
55
| This code can be rewritten automatically under -rewrite -source 3.7-migration.
6-
| To disable the warning, please use the following option:
6+
| To disable the warning, please use the following option:
77
| "-Wconf:msg=Implicit parameters should be provided with a `using` clause:s"

Diff for: tests/rewrites/i22440.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
def foo(implicit x: Int) = ()
44
val _ = foo(using 1)
5-
val _ = foo (using 1)
5+
val _ = foo(using 1)

Diff for: tests/rewrites/i22927.check

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Input(x: String)
2+
3+
trait Decoder[T]
4+
5+
object Decoder:
6+
inline def apply[T](implicit f: () => Unit): Decoder[T] = ???
7+
8+
object Input:
9+
given Decoder[Input] = Decoder(using { () =>
10+
Input("")
11+
})

Diff for: tests/rewrites/i22927.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Input(x: String)
2+
3+
trait Decoder[T]
4+
5+
object Decoder {
6+
inline def apply[T](implicit f: () => Unit): Decoder[T] = ???
7+
}
8+
9+
object Input {
10+
given Decoder[Input] = Decoder { () =>
11+
Input("")
12+
}
13+
}

Diff for: tests/rewrites/i22927b.check

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
def foo(implicit f: () => Unit): Unit = ???
3+
def bar(a: Int)(implicit f: () => Unit): Unit = ???
4+
5+
@main def main =
6+
// Trailing lambdas with braces:
7+
foo(using { () => 43 })
8+
foo(using { () => val x = 42; 43 })
9+
foo(using { () => val x = 42; 43 })
10+
foo(using {() => val x = 42; 43})
11+
bar(1)(using { () =>
12+
val x = 42
13+
43 })
14+
15+
// Parentheses:
16+
foo(using () => 43 )
17+
foo(using () =>
18+
val x = 42
19+
43
20+
)
21+
foo(using () =>
22+
val x = 42
23+
43
24+
)
25+
foo(using () =>
26+
val x = 42
27+
43
28+
)
29+
bar(1)(using () =>
30+
val x = 42
31+
43 )
32+
33+
// Trailing lambdas with column and indentation:
34+
foo(using () =>
35+
43)
36+
foo(using () =>
37+
val x = 42
38+
43)
39+
foo(using () =>
40+
val x = 42
41+
43)
42+
foo(using () =>
43+
val x = 42
44+
43)
45+
foo(using () =>
46+
val x = 42
47+
43)
48+
bar(1)(using () =>
49+
val x = 42
50+
43)
51+

0 commit comments

Comments
 (0)