scala3
scala3 copied to clipboard
Add Synthetics section to SemanticDB
https://scalameta.org/docs/semanticdb/specification.html#synthetic
"Synthetics" is a section of a TextDocument that stores trees added by compilers that do not appear in the original source. Examples include inferred type arguments, implicit parameters, or desugarings of for loops.
One of the main consumers of Synthetics
information of SemanticDB is decraotion feature of metals, and once Scala3 extract these information, those features are available for Scala3 in Metals.
TODO
- Unique to Scala3
- [ ] Context parameter application
- [x] https://github.com/lampepfl/dotty/pull/13288
- [ ] https://github.com/scalameta/scalameta/issues/2460
- [ ] Anonymous Context Parameters
- https://github.com/scalameta/scalameta/issues/2458
- [ ] Extension method?
- [ ] Context parameter application
- Scala2 compatible
- [ ] Inferred method application
- https://github.com/tanishiking/dotty/pull/5
- [ ] Inferred Type Application
- [x] Implicit parameters
- https://github.com/lampepfl/dotty/pull/13288
- [x] Implicit Conversions
- https://github.com/lampepfl/dotty/pull/13288
- [ ] macro expansion (low priority)
- [ ] For loop desugaring (low priority)
- [ ] Inferred method application
If there's something we should add to Synthetic section, please let me know :)
Using parameter application
def foo(using x: Int) = ???
def bar(using x: Int) = foo
// synthetic
def foo(using x: Int): Nothing = ???
def bar(using x: Int): Nothing = foo(x)
what should we do for anonymous context params?
def foo(using Int) = ???
def bar(using Int) = foo
// synthetic
def foo(using x$1: Int): Nothing = ???
def bar(using x$1: Int): Nothing = classes.foo(x$1)
should we add x$1
to synthetics?
Synthetic(
<foo>,
ApplyTree(
OriginalTree(<foo>),
List(
IdTree(<x>),
)
))
)
Anonymous context params
def foo(using Int) = ???
// synthetic
def foo(using x$1: Int) = ???
Synthetic(
<using>,
IdTree(<x$1>)
)
Extension method application?
Wondering how should we design the Synthetics Tree for the extension method...
extension (x: Int)
def plus(y: Int) = x + y
def main() = 1.plus(3)
// synthetic
def main() = plus(1)(3)
Maybe we don't need to extract synthetics for the extension method at this moment, because the main consumer of the synthetics section of SemancticDB is metals's Decoration feature.
While extracting Implicit conversion for Scala2 is important for implicit decoration's readability, showing plus(1)(3)
for the extension method doesn't help developers to read those programs.
Scala2 compatible synthetics
Inferred method application
val x = List(1)
val List(a, b) = List(1, 2)
// synthetics
val x = List.apply[Int](1)
val List.unapplySeq[Int](a, b) = List.apply[Int](1, 2)
Synthetic(
<List>,
TypeApplyTree(
SelectTree(
OriginalTree(<List>),
Some(IdTree(<List.apply>))),
List(TypeRef(None, <Int>, List()))
)
)
Synthetic(
<List>,
TypeApplyTree(
SelectTree(
OriginalTree(<List>),
Some(IdTree(<SeqFactory.unapplySeq>))),
List(TypeRef(None, <Nothing>, List()))
)
)
Inferred Type Application
List(1).map(_ + 2)
1 #:: 2 #:: Stream.empty
// synthetic
List.apply[Int](1).map[Int](_ + 2)
1 #:: 2 #:: Stream.empty[Int]
Synthetic(
<List>,
TypeApplyTree(
SelectTree(
OriginalTree(<List>),
Some(IdTree(<List.apply>))),
List(TypeRef(None, <Int>, List()))
)
)
Synthetic(
<List(1).map>,
TypeApplyTree(
OriginalTree(<List(1).map>),
List(
TypeRef(None, <Int>, List()),
TypeRef(None, <List>,
List(TypeRef(None, <Int>, List()))))
)
)
Synthetic(
<#:: 2 #:: Stream.empty>,
TypeApplyTree(
OriginalTree(<#:: 2 #:: Stream.empty>),
List(
TypeRef(None, <Int>, List())))
)
Implicit parameters
Seq.apply[Int](1,2,3).sorted[Int]
// synthetic
Seq.apply[Int](1,2,3).sorted[Int](Ordering.Int)
Synthetic(
<Seq.apply[Int](1,2,3).sorted[Int]>,
ApplyTree(
OriginalTree(<Seq.apply[Int](1,2,3).sorted[Int]),
List(
IdTree(<Ordering.Int>),
)
))
)
implicit conversion
"fooo".stripPrefix("o")
// synthetic
augmentString("fooo").stripPrefix("o")
Synthetic(
<"fooo">,
ApplyTree(
IdTree(<Predef.augmentString>),
List(OriginalTree(<"fooo">)))
)
Macro Expansion
Array.empty[Int]
Synthetic(
<Array.empty[Int]>,
ApplyTree(
OriginalTree(<Array.empty[Int]>),
List(
MacroExpansionTree(
IdTree(<ClassTag.Int>),
TypeRef(None, <ClassTag>,
List(TypeRef(None, <Int>, List())))))))
For loop desugaring
object Test {
for {
i <- 1 to 10
j <- 0 until 10
if i % 2 == 0
} yield (i, j)
}
documents {
schema: SEMANTICDB4
synthetics {
range {
start_line: 1
start_character: 2
end_line: 5
end_character: 16
}
tree {
apply_tree {
function {
type_apply_tree {
function {
select_tree {
qualifier {
original_tree {
range {
start_line: 2
start_character: 9
end_line: 2
end_character: 16
}
}
}
id {
symbol: "scala/collection/StrictOptimizedIterableOps#flatMap()."
}
}
}
type_arguments {
type_ref {
symbol: "scala/Tuple2#"
type_arguments {
type_ref {
symbol: "scala/Int#"
}
}
type_arguments {
type_ref {
symbol: "scala/Int#"
}
}
}
}
}
}
arguments {
function_tree {
parameters {
symbol: "local0"
}
body {
apply_tree {
function {
type_apply_tree {
function {
select_tree {
qualifier {
apply_tree {
function {
select_tree {
qualifier {
original_tree {
range {
start_line: 3
start_character: 9
end_line: 3
end_character: 19
}
}
}
id {
symbol: "scala/collection/IterableOps#withFilter()."
}
}
}
arguments {
function_tree {
parameters {
symbol: "local1"
}
body {
original_tree {
range {
start_line: 4
start_character: 7
end_line: 4
end_character: 17
}
}
}
}
}
}
}
id {
symbol: "scala/collection/WithFilter#map()."
}
}
}
type_arguments {
type_ref {
symbol: "scala/Tuple2#"
type_arguments {
type_ref {
symbol: "scala/Int#"
}
}
type_arguments {
type_ref {
symbol: "scala/Int#"
}
}
}
}
}
}
arguments {
function_tree {
parameters {
symbol: "local1"
}
body {
original_tree {
range {
start_line: 5
start_character: 10
end_line: 5
end_character: 16
}
}
}
}
}
}
}
}
}
}
}
}
synthetics {
range {
start_line: 2
start_character: 9
end_line: 2
end_character: 10
}
tree {
apply_tree {
function {
id_tree {
symbol: "scala/LowPriorityImplicits#intWrapper()."
}
}
arguments {
original_tree {
range {
start_line: 2
start_character: 9
end_line: 2
end_character: 10
}
}
}
}
}
}
synthetics {
range {
start_line: 3
start_character: 9
end_line: 3
end_character: 10
}
tree {
apply_tree {
function {
id_tree {
symbol: "scala/LowPriorityImplicits#intWrapper()."
}
}
arguments {
original_tree {
range {
start_line: 3
start_character: 9
end_line: 3
end_character: 10
}
}
}
}
}
}
}