Skip to content

Commit 66a3076

Browse files
Add spans to every source tree node (#1039)
Working towards completing the span transition, this PR adds the remaining spans to syntax tree nodes. In particular, this PR adds spans to: * Feature flags * `ExternBody` * `Include` * `Param` * `Stmt` * `Term` * `Type` * `MatchPattern` * `Implementation` * `Handler` * `OpClause` We still need to tear down the legacy `Positions` map and switch everything over to use the new spans. There are also still a few missing spans (`Span.missing`), but these should only affect places where it is non-trivial to add spans and where we also didn't have spans before.
1 parent bb2c2c1 commit 66a3076

22 files changed

+689
-554
lines changed

effekt/jvm/src/main/scala/effekt/Repl.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
108108
}
109109
output.emitln("")
110110

111-
runFrontend(StringSource(""), module.make(StringSource(""), UnitLit()), config) { cu =>
111+
runFrontend(StringSource(""), module.make(StringSource(""), UnitLit(Span.missing)), config) { cu =>
112112
module.definitions.foreach {
113113
case u: Def =>
114114
outputCode(DeclPrinter(context.symbolOf(u)), config)
@@ -217,7 +217,7 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
217217

218218
case d: Def =>
219219
val extendedDefs = module + d
220-
val decl = extendedDefs.make(source, UnitLit())
220+
val decl = extendedDefs.make(source, UnitLit(Span.missing))
221221

222222
runFrontend(source, decl, config) { cu =>
223223
module = extendedDefs
@@ -311,7 +311,7 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
311311
*/
312312
def make(source: Source, expr: Term): ModuleDecl = {
313313

314-
val body = Return(expr)
314+
val body = Return(expr, expr.span.synthesized)
315315
val fakeSpan = Span(source, 0, 0, origin = Origin.Synthesized)
316316
val fullSpan = Span(source, 0, source.content.length, origin = Origin.Synthesized)
317317
ModuleDecl("interactive", includes,
@@ -321,7 +321,7 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
321321

322322
def makeEval(source: Source, expr: Term): ModuleDecl = {
323323
val fakeSpan = Span(source, 0, 0, origin = Origin.Synthesized)
324-
make(source, Call(IdTarget(IdRef(List(), "println", fakeSpan)), Nil, List(expr), Nil))
324+
make(source, Call(IdTarget(IdRef(List(), "println", fakeSpan)), Nil, List(expr), Nil, fakeSpan))
325325
}
326326
}
327327
lazy val emptyModule = ReplModule(Nil, Nil)

effekt/jvm/src/main/scala/effekt/Server.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ class Server(config: EffektConfig, compileOnChange: Boolean=false) extends Langu
479479
contentTpe <- C.inferredTypeOption(hole.stmts)
480480
if holeTpe == contentTpe
481481
res <- hole match {
482-
case Hole(id, source.Return(exp), span) => for {
482+
case Hole(id, source.Return(exp, _), span) => for {
483483
text <- positions.textOf(exp)
484484
} yield EffektCodeAction("Close hole", span, text)
485485

effekt/jvm/src/test/scala/effekt/LSPTests.scala

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,7 @@ class LSPTests extends FunSuite {
11871187
didOpenParams.setTextDocument(source)
11881188
server.getTextDocumentService().didOpen(didOpenParams)
11891189

1190+
11901191
val expectedIRContents =
11911192
raw"""ModuleDecl(
11921193
| test,
@@ -1224,17 +1225,36 @@ class LSPTests extends FunSuite {
12241225
| Synthesized()
12251226
| )
12261227
| ),
1227-
| Return(Literal((), ValueTypeApp(Unit_405, Nil))),
1228+
| Return(
1229+
| Literal(
1230+
| (),
1231+
| ValueTypeApp(Unit_whatever, Nil),
1232+
| Span(
1233+
| StringSource(def main() = <>, file://test.effekt),
1234+
| 13,
1235+
| 15,
1236+
| Synthesized()
1237+
| )
1238+
| ),
1239+
| Span(
1240+
| StringSource(def main() = <>, file://test.effekt),
1241+
| 13,
1242+
| 15,
1243+
| Synthesized()
1244+
| )
1245+
| ),
12281246
| Span(StringSource(def main() = <>, file://test.effekt), 13, 15, Real())
1229-
| )
1247+
| ),
1248+
| Span(StringSource(def main() = <>, file://test.effekt), 13, 15, Real())
12301249
| ),
12311250
| None(),
12321251
| Span(StringSource(def main() = <>, file://test.effekt), 0, 15, Real())
12331252
| )
12341253
| ),
12351254
| None(),
12361255
| Span(StringSource(def main() = <>, file://test.effekt), 0, 15, Real())
1237-
|)""".stripMargin
1256+
|)
1257+
|""".stripMargin
12381258

12391259
val receivedIRContent = client.receivedIR()
12401260
assertEquals(receivedIRContent.length, 1)

effekt/jvm/src/test/scala/effekt/RecursiveDescentTests.scala

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,20 @@ class RecursiveDescentTests extends munit.FunSuite {
270270
{
271271
val (source, pos) =
272272
raw"""loop { f }
273-
|↑ ↑ ↑↑
273+
|↑ ↑ ↑↑
274274
|""".sourceAndPositions
275275

276276
assertEquals(
277277
parseExpr(source.content),
278-
Call(IdTarget(IdRef(Nil, "loop", Span(source, pos(0), pos(1)))), Nil, Nil, List(Var(IdRef(Nil, "f", Span(source, pos(2), pos(3)))))))
278+
Call(
279+
IdTarget(IdRef(Nil, "loop", Span(source, pos(0), pos(1)))),
280+
Nil, Nil,
281+
List(
282+
Var(IdRef(Nil, "f", Span(source, pos(2), pos(3))), Span(source, pos(2), pos(3)))
283+
),
284+
Span(source, pos(0), pos.last)
285+
)
286+
)
279287
}
280288

281289
assertNotEqualModuloSpans(
@@ -294,7 +302,12 @@ class RecursiveDescentTests extends munit.FunSuite {
294302
assertEquals(
295303
parseExpr(source.content),
296304
// At the moment uniform function call syntax is always a method call
297-
MethodCall(Var(IdRef(Nil, "foo", Span(source, pos(0), pos(1)))), IdRef(Nil, "bar", Span(source, pos(2), pos(3))), Nil, Nil, Nil))
305+
MethodCall(
306+
Var(IdRef(Nil, "foo", Span(source, pos(0), pos(1))), Span(source, pos(0), pos(1))),
307+
IdRef(Nil, "bar", Span(source, pos(2), pos(3))), Nil, Nil, Nil,
308+
Span(source, pos(0), pos.last)
309+
)
310+
)
298311
}
299312

300313
parseExpr("resume(42)")
@@ -336,7 +349,7 @@ class RecursiveDescentTests extends munit.FunSuite {
336349
|""".sourceAndPosition
337350
val b = parseExpr(source.content)
338351
b match {
339-
case Term.Box(c, _) => assertEquals(c.span, Span(source, pos, pos, Synthesized))
352+
case Term.Box(c, _, _) => assertEquals(c.span, Span(source, pos, pos, Synthesized))
340353
case other =>
341354
throw new IllegalArgumentException(s"Expected Box but got ${other.getClass.getSimpleName}")
342355
}
@@ -379,28 +392,28 @@ class RecursiveDescentTests extends munit.FunSuite {
379392
raw"""map
380393
|↑ ↑
381394
|""".sourceAndSpan
382-
assertEquals(parseExpr(source.content), Var(IdRef(List(), "map", span)))
395+
assertEquals(parseExpr(source.content), Var(IdRef(List(), "map", span), span))
383396
}
384397
{
385398
val (source, span) =
386399
raw"""list::map
387400
|↑ ↑
388401
|""".sourceAndSpan
389-
assertEquals(parseExpr(source.content), Var(IdRef(List("list"), "map", span)))
402+
assertEquals(parseExpr(source.content), Var(IdRef(List("list"), "map", span), span))
390403
}
391404
{
392405
val (source, span) =
393406
raw"""list::internal::map
394407
|↑ ↑
395408
|""".sourceAndSpan
396-
assertEquals(parseExpr(source.content), Var(IdRef(List("list", "internal"), "map", span)))
409+
assertEquals(parseExpr(source.content), Var(IdRef(List("list", "internal"), "map", span), span))
397410
}
398411
{
399412
val (source, span) =
400413
raw"""list::internal::test
401414
|↑ ↑
402415
|""".sourceAndSpan
403-
assertEquals(parseExpr(source.content), Var(IdRef(List("list", "internal"), "test", span)))
416+
assertEquals(parseExpr(source.content), Var(IdRef(List("list", "internal"), "test", span), span))
404417
}
405418
}
406419

@@ -481,6 +494,34 @@ class RecursiveDescentTests extends munit.FunSuite {
481494
parseStmts("val g: () => Unit / Exc at {exc} = fun() { closure() }; ()")
482495
}
483496

497+
test("Pattern-matching val parses with correct span") {
498+
val (source, pos) =
499+
raw"""val (left, right) = list; return left
500+
| ↑↑ ↑ ↑ ↑↑ ↑ ↑ ↑ ↑ ↑
501+
|""".sourceAndPositions
502+
val expected = Return(Match(
503+
List(Var(IdRef(List(), "list", Span(source,pos(6),pos(7))),Span(source,pos(6),pos(7)))),
504+
List(
505+
MatchClause(
506+
TagPattern(
507+
IdRef(List("effekt"),"Tuple2",Span(source,pos(0),pos(5),Synthesized)),
508+
List(AnyPattern(IdDef("left", Span(source,pos(1),pos(2))), Span(source,pos(1),pos(2))),
509+
AnyPattern(IdDef("right",Span(source,pos(3),pos(4))), Span(source,pos(3),pos(4)))),
510+
Span(source,pos(0),pos(5))
511+
),
512+
List(),
513+
Return(Var(IdRef(List(),"left",Span(source,pos(9),pos(10))),
514+
Span(source,pos(9),pos(10))),
515+
Span(source,pos(8),pos(10))),
516+
Span(source,pos(0),pos(7))
517+
)
518+
),
519+
None,
520+
Span(source,0,pos(7), Synthesized)
521+
),Span(source,0,pos.last, Synthesized));
522+
assertEquals(parseStmts(source.content), expected)
523+
}
524+
484525
test("Semicolon insertion") {
485526
parseStmts("f(); return x")
486527
parseStmts(
@@ -521,23 +562,27 @@ class RecursiveDescentTests extends munit.FunSuite {
521562
parseMatchPattern("Cons(x, y)")
522563
assertEqualModuloSpans(
523564
parseMatchPattern("_"),
524-
IgnorePattern())
565+
IgnorePattern(Span.missing))
525566
parseMatchPattern("Cons(x, Cons(x, Nil()))")
526567

527568
{
528569
val (source, pos) =
529570
raw"""(left, Cons(x, right))
530-
|↑↑ ↑ ↑ ↑↑↑ ↑ ↑
571+
|↑↑ ↑ ↑ ↑↑↑ ↑ ↑
531572
|""".sourceAndPositions
532573
assertEquals(
533574
parseMatchPattern(source.content),
534-
TagPattern(IdRef(List("effekt"), "Tuple2", Span(source, pos(0), pos(9), Synthesized)),
535-
List(AnyPattern(IdDef("left", Span(source, pos(1), pos(2)))),
575+
TagPattern(IdRef(List("effekt"), "Tuple2", Span(source, pos(0), pos.last, Synthesized)),
576+
List(AnyPattern(IdDef("left", Span(source, pos(1), pos(2))), Span(source, pos(1), pos(2))),
536577
TagPattern(
537578
IdRef(List(), "Cons", Span(source, pos(3), pos(4))),
538-
List(AnyPattern(IdDef("x", Span(source, pos(5), pos(6)))),
539-
AnyPattern(IdDef("right", Span(source, pos(7), pos(8)))))
540-
))))
579+
List(AnyPattern(IdDef("x", Span(source, pos(5), pos(6))), Span(source, pos(5), pos(6))),
580+
AnyPattern(IdDef("right", Span(source, pos(7), pos(8))), Span(source, pos(7), pos(8)))),
581+
Span(source, pos(3), pos(9))
582+
)),
583+
Span(source, pos(0), pos.last)
584+
)
585+
)
541586
}
542587
}
543588

@@ -626,14 +671,15 @@ class RecursiveDescentTests extends munit.FunSuite {
626671
|""".sourceAndPositions
627672
assertEquals(
628673
parseBlockType(source.content),
629-
FunctionType(Many.empty(Span(source, pos(0), pos(1))),
674+
FunctionType(Many.empty(Span(source, pos(0), pos(0))),
630675
Many(List(
631676
TypeRef(IdRef(Nil, "Int", Span(source, pos(1), pos(2))), Many.empty(Span(source, pos(2), pos(2))), Span(source, pos(1), pos(2))),
632677
TypeRef(IdRef(Nil, "String", Span(source, pos(3), pos(4))), Many.empty(Span(source, pos(4), pos(4))), Span(source, pos(3), pos(4)))
633678
), Span(source, pos(0), pos(5))),
634679
Many.empty(Span(source, pos(5), pos(5))),
635680
TypeRef(IdRef(Nil, "Int", Span(source, pos(6), pos(7))), Many.empty(Span(source, pos(7), pos(7))), Span(source, pos(6), pos(7))),
636-
Effects(Nil, Span(source, pos.last, pos.last, Synthesized))
681+
Effects(Nil, Span(source, pos.last, pos.last, Synthesized)),
682+
Span(source, pos(0), pos.last)
637683
)
638684
)
639685
}
@@ -710,13 +756,17 @@ class RecursiveDescentTests extends munit.FunSuite {
710756

711757
test("Implementations") {
712758
{
713-
val (source, span) =
759+
val (source, pos) =
714760
raw"""Foo {}
715-
|↑ ↑
716-
|""".sourceAndSpan
761+
|↑
762+
|""".sourceAndPositions
717763
assertEquals(
718764
parseImplementation(source.content),
719-
Implementation(TypeRef(IdRef(Nil, "Foo", span), Many.empty(Span(source, span.to, span.to)), span), Nil))
765+
Implementation(
766+
TypeRef(IdRef(Nil, "Foo", Span(source, pos(0), pos(1))), Many.empty(Span(source, pos(1), pos(1))), Span(source, pos(0), pos(1))),
767+
Nil,
768+
Span(source, pos(0), pos(2)))
769+
)
720770
}
721771
parseImplementation("Foo[T] {}")
722772
parseImplementation("Foo[T] { def bar() = 42 }")
@@ -734,14 +784,18 @@ class RecursiveDescentTests extends munit.FunSuite {
734784
{
735785
val (source, pos) =
736786
raw"""Foo { 43 }
737-
|↑ ↑
787+
|↑ ↑ ↑ ↑ ↑
738788
|""".sourceAndPositions
739789
assertEquals(
740790
parseImplementation(source.content),
741791
Implementation(
742792
TypeRef(IdRef(Nil, "Foo", Span(source, pos(0), pos(1))), Many.empty(Span(source, pos(1), pos(1))), Span(source, pos(0), pos(1), Synthesized)),
743793
List(OpClause(IdRef(Nil, "Foo", Span(source, pos(0), pos(1), Synthesized)), Nil, Nil, Nil, None,
744-
Return(Literal(43, symbols.builtins.TInt)), IdDef("resume", Span(source, pos(1), pos(1)))))))
794+
Return(Literal(43, symbols.builtins.TInt, Span(source, pos(2), pos(3))), Span(source, pos(2), pos(3))), IdDef("resume", Span(source, pos(1), pos(1))),
795+
Span(source, pos(0), pos(3), Synthesized))
796+
),
797+
Span(source, pos(0), pos.last)
798+
))
745799
}
746800
}
747801

@@ -768,6 +822,21 @@ class RecursiveDescentTests extends munit.FunSuite {
768822
)
769823
}
770824

825+
test("Try handler capability parses with correct span") {
826+
val (source, pos) =
827+
raw"""try { 42 } with eff: Eff { 42 }
828+
| ↑ ↑
829+
|""".sourceAndPositions
830+
831+
val tryExpr = parseTry(source.content) match {
832+
case t: Term.TryHandle => t
833+
case other =>
834+
throw new IllegalArgumentException(s"Expected Try but got ${other.getClass.getSimpleName}")
835+
}
836+
837+
assertEquals(tryExpr.handlers.head.capability.get.span, Span(source, pos(0), pos(1), Synthesized))
838+
}
839+
771840
test("Type definition") {
772841
parseDefinition("type A = Int")
773842
parseDefinition("type A[X] = Int")
@@ -866,7 +935,11 @@ class RecursiveDescentTests extends munit.FunSuite {
866935

867936
assertEquals(
868937
parseDefinition(source.content),
869-
DefDef(IdDef("foo", Span(source, pos(0), pos(1))), None, Var(IdRef(Nil, "f", Span(source, pos(2), pos(3)))), None, Span(source, 0, pos.last)))
938+
DefDef(
939+
IdDef("foo", Span(source, pos(0), pos(1))),
940+
None,
941+
Var(IdRef(Nil, "f", Span(source, pos(2), pos(3))), Span(source, pos(2), pos(3))),
942+
None, Span(source, 0, pos.last)))
870943
}
871944

872945
parseDefinition(
@@ -1011,7 +1084,7 @@ class RecursiveDescentTests extends munit.FunSuite {
10111084
val definition = parseStmts(source.content)
10121085

10131086
val regDef = definition match {
1014-
case DefStmt(rd@RegDef(id, annot, region, binding, doc, span), _) => rd
1087+
case DefStmt(rd@RegDef(id, annot, region, binding, doc, span), _, _) => rd
10151088
case other =>
10161089
throw new IllegalArgumentException(s"Expected RegDef but got ${other.getClass.getSimpleName}")
10171090
}
@@ -1028,7 +1101,7 @@ class RecursiveDescentTests extends munit.FunSuite {
10281101
val definition = parseStmts(source.content)
10291102

10301103
val varDef = definition match {
1031-
case DefStmt(vd@VarDef(id, annot, binding, doc, span), _) => vd
1104+
case DefStmt(vd@VarDef(id, annot, binding, doc, span), _, _) => vd
10321105
case other =>
10331106
throw new IllegalArgumentException(s"Expected VarDef but got ${other.getClass.getSimpleName}")
10341107
}
@@ -1247,10 +1320,10 @@ class RecursiveDescentTests extends munit.FunSuite {
12471320
}
12481321

12491322
test("Extern effect‐body parses with correct span") {
1250-
val (source, span) =
1323+
val (source, pos) =
12511324
raw"""extern def foo(): Int = default { foo() }
1252-
|↑
1253-
|""".sourceAndSpan
1325+
|↑
1326+
|""".sourceAndPositions
12541327

12551328
val definition = parseToplevel(source.content)
12561329

@@ -1260,7 +1333,9 @@ class RecursiveDescentTests extends munit.FunSuite {
12601333
throw new IllegalArgumentException(s"Expected ExternDef but got ${other.getClass.getSimpleName}")
12611334
}
12621335

1263-
assertEquals(extDef.span, span)
1336+
assertEquals(extDef.bodies.head.span, Span(source, pos(1), pos.last))
1337+
assertEquals(extDef.bodies.head.featureFlag.span, Span(source, pos(1), pos(2)))
1338+
assertEquals(extDef.span, Span(source, pos(0), pos.last))
12641339
}
12651340

12661341
test("Toplevel definitions") {

0 commit comments

Comments
 (0)