Skip to content

Commit a26a2c3

Browse files
authored
Merge pull request #14608 from tanishiking/typelambda-matchtype-2
Support MatchType for SemanticDB
2 parents 4e9f610 + ba24fe1 commit a26a2c3

14 files changed

+697
-108
lines changed

compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala

+22-14
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,12 @@ class ExtractSemanticDB extends Phase:
234234
val sym = tree.symbol.adjustIfCtorTyparam
235235
if qualSpan.exists && qualSpan.hasLength then
236236
traverse(qual)
237-
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
237+
if (sym != NoSymbol)
238+
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
239+
else
240+
qual.symbol.info.lookupSym(tree.name).foreach(sym =>
241+
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
242+
)
238243
case tree: Import =>
239244
if tree.span.exists && tree.span.hasLength then
240245
traverseChildren(tree)
@@ -422,19 +427,22 @@ class ExtractSemanticDB extends Phase:
422427
else
423428
val symkinds = mutable.HashSet.empty[SymbolKind]
424429
tree match
425-
case tree: ValDef =>
426-
if !tree.symbol.is(Param) then
427-
symkinds += (if tree.mods is Mutable then SymbolKind.Var else SymbolKind.Val)
428-
if tree.rhs.isEmpty && !tree.symbol.isOneOf(TermParam | CaseAccessor | ParamAccessor) then
429-
symkinds += SymbolKind.Abstract
430-
case tree: DefDef =>
431-
if tree.isSetterDef then
432-
symkinds += SymbolKind.Setter
433-
else if tree.rhs.isEmpty then
434-
symkinds += SymbolKind.Abstract
435-
case tree: Bind =>
436-
symkinds += SymbolKind.Val
437-
case _ =>
430+
case tree: ValDef =>
431+
if !tree.symbol.is(Param) then
432+
symkinds += (if tree.mods is Mutable then SymbolKind.Var else SymbolKind.Val)
433+
if tree.rhs.isEmpty && !tree.symbol.isOneOf(TermParam | CaseAccessor | ParamAccessor) then
434+
symkinds += SymbolKind.Abstract
435+
case tree: DefDef =>
436+
if tree.isSetterDef then
437+
symkinds += SymbolKind.Setter
438+
else if tree.rhs.isEmpty then
439+
symkinds += SymbolKind.Abstract
440+
// if symbol isType, it's type variable
441+
case tree: Bind if (!tree.symbol.isType) =>
442+
symkinds += SymbolKind.Val
443+
case tree: Bind if (tree.symbol.isType) =>
444+
symkinds += SymbolKind.TypeVal
445+
case _ =>
438446
symkinds.toSet
439447

440448
private def ctorParams(

compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala

+6-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@ class SymbolInformationPrinter (symtab: PrinterSymtab):
191191
s"=> ${normal(utpe)}"
192192
case RepeatedType(utpe) =>
193193
s"${normal(utpe)}*"
194-
case _ =>
194+
case MatchType(scrutinee, cases) =>
195+
val casesStr = cases.map { caseType =>
196+
s"${pprint(caseType.key)} => ${pprint(caseType.body)}"
197+
}.mkString(", ")
198+
s"${pprint(scrutinee)} match { ${casesStr} }"
199+
case x =>
195200
"<?>"
196201

197202
def normal(tpe: Type): String = tpe match

compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala

+21-10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import core.StdNames.nme
1212
import SymbolInformation.{Kind => k}
1313
import dotty.tools.dotc.util.SourceFile
1414
import dotty.tools.dotc.util.Spans.Span
15+
import dotty.tools.dotc.core.Names.Designator
1516

1617
import java.lang.Character.{isJavaIdentifierPart, isJavaIdentifierStart}
1718

@@ -34,23 +35,30 @@ object Scala3:
3435
val (endLine, endCol) = lineCol(span.end)
3536
Some(Range(startLine, startCol, endLine, endCol))
3637

37-
def namePresentInSource(sym: Symbol, span: Span, source:SourceFile)(using Context): Boolean =
38+
def namePresentInSource(desig: Designator, span: Span, source:SourceFile)(using Context): Boolean =
3839
if !span.exists then false
3940
else
4041
val content = source.content()
4142
val (start, end) =
4243
if content.lift(span.end - 1).exists(_ == '`') then
4344
(span.start + 1, span.end - 1)
4445
else (span.start, span.end)
46+
// println(s"${start}, $end")
4547
val nameInSource = content.slice(start, end).mkString
4648
// for secondary constructors `this`
47-
if sym.isConstructor && nameInSource == nme.THISkw.toString then
48-
true
49-
else
50-
val target =
51-
if sym.isPackageObject then sym.owner
52-
else sym
53-
nameInSource == target.name.stripModuleClassSuffix.lastPart.toString
49+
desig match
50+
case sym: Symbol =>
51+
if sym.isConstructor && nameInSource == nme.THISkw.toString then
52+
true
53+
else
54+
val target =
55+
if sym.isPackageObject then sym.owner
56+
else sym
57+
nameInSource == target.name.stripModuleClassSuffix.lastPart.toString
58+
case name: Name =>
59+
// println(nameInSource)
60+
// println(name.mangledString)
61+
nameInSource == name.mangledString
5462

5563
sealed trait FakeSymbol {
5664
private[Scala3] var sname: Option[String] = None
@@ -153,12 +161,13 @@ object Scala3:
153161
enum SymbolKind derives CanEqual:
154162
kind =>
155163

156-
case Val, Var, Setter, Abstract
164+
case Val, Var, Setter, Abstract, TypeVal
157165

158166
def isVar: Boolean = kind match
159167
case Var | Setter => true
160168
case _ => false
161169
def isVal: Boolean = kind == Val
170+
def isTypeVal: Boolean = kind == TypeVal
162171
def isVarOrVal: Boolean = kind.isVar || kind.isVal
163172

164173
end SymbolKind
@@ -309,7 +318,9 @@ object Scala3:
309318
props |= SymbolInformation.Property.IMPLICIT.value
310319
if sym.is(Lazy, butNot=Module) then
311320
props |= SymbolInformation.Property.LAZY.value
312-
if sym.isAllOf(Case | Module) || sym.is(CaseClass) || sym.isAllOf(EnumCase) then
321+
if sym.isAllOf(Case | Module) ||
322+
(sym.is(CaseClass) && !symkinds.exists(_.isTypeVal)) || // `t` of `case List[t] =>` (which has `CaseClass` flag) shouldn't be `CASE`
323+
sym.isAllOf(EnumCase) then
313324
props |= SymbolInformation.Property.CASE.value
314325
if sym.is(Covariant) then
315326
props |= SymbolInformation.Property.COVARIANT.value

compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala

+90-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import collection.mutable
1515

1616
import dotty.tools.dotc.{semanticdb => s}
1717
import Scala3.{FakeSymbol, SemanticSymbol, WildcardTypeSymbol, TypeParamRefSymbol, TermParamRefSymbol, RefinementSymbol}
18+
import dotty.tools.dotc.core.Names.Designator
1819

1920
class TypeOps:
2021
import SymbolScopeOps._
@@ -33,7 +34,7 @@ class TypeOps:
3334
)(using Context): Option[Symbol] =
3435
symtab.get((binder, name))
3536

36-
extension [T <: LambdaType](symtab: mutable.Map[(T, Name), Symbol])
37+
extension [T <: LambdaType | RefinedType](symtab: mutable.Map[(T, Name), Symbol])
3738
private def lookupOrErr(
3839
binder: T,
3940
name: Name,
@@ -53,6 +54,8 @@ class TypeOps:
5354

5455
private def symbolNotFound(binder: Type, name: Name, parent: Symbol)(using ctx: Context): Unit =
5556
warn(s"Ignoring ${name} of symbol ${parent}, type ${binder}")
57+
private def symbolNotFound(name: Name, parent: Symbol)(using ctx: Context): Unit =
58+
warn(s"Ignoring ${name} of symbol ${parent}")
5659

5760
private def warn(msg: String)(using ctx: Context): Unit =
5861
report.warning(
@@ -63,6 +66,23 @@ class TypeOps:
6366
fakeSymbols.add(sym)
6467

6568
extension (tpe: Type)
69+
def lookupSym(name: Name)(using Context): Option[Symbol] = {
70+
def loop(ty: Type): Option[Symbol] = ty match
71+
case rt: RefinedType =>
72+
refinementSymtab.lookup(rt, name).orElse(
73+
loop(rt.parent)
74+
)
75+
case rec: RecType =>
76+
loop(rec.parent)
77+
case AndType(tp1, tp2) =>
78+
loop(tp1).orElse(loop(tp2))
79+
case OrType(tp1, tp2) =>
80+
loop(tp1).orElse(loop(tp2))
81+
case _ =>
82+
None
83+
loop(tpe.dealias)
84+
}
85+
6686
def toSemanticSig(using LinkMode, Context, SemanticSymbolBuilder)(sym: Symbol): s.Signature =
6787
def enterParamRef(tpe: Type): Unit =
6888
tpe match {
@@ -81,6 +101,10 @@ class TypeOps:
81101
else
82102
enterParamRef(lam.resType)
83103

104+
// for CaseType `case Array[t] => t` which is represented as [t] =>> MatchCase[Array[t], t]
105+
case m: MatchType =>
106+
m.cases.foreach(enterParamRef)
107+
84108
// for class constructor
85109
// class C[T] { ... }
86110
case cls: ClassInfo if sym.info.isInstanceOf[LambdaType] =>
@@ -137,6 +161,12 @@ class TypeOps:
137161
enterRefined(expr.resType)
138162
case m: LambdaType =>
139163
enterRefined(m.resType)
164+
case AndType(t1, t2) =>
165+
enterRefined(t1)
166+
enterRefined(t2)
167+
case OrType(t1, t2) =>
168+
enterRefined(t1)
169+
enterRefined(t2)
140170
case _ => ()
141171
}
142172
if sym.exists && sym.owner.exists then
@@ -223,16 +253,42 @@ class TypeOps:
223253
val stpe = loop(tpe)
224254
s.ByNameType(stpe)
225255

226-
case TypeRef(pre, sym: Symbol) =>
256+
// sym of `TypeRef(_, sym)` may not be a Symbol but Name in some cases
257+
// e.g. in MatchType,
258+
// case x *: xs => x *: Concat[xs, Ys]
259+
// x and xs should have a typebounds <: Any, >: Nothing
260+
// but Any (and Nothing) are represented as TypeRef(<scala>, "Any" <- Name)
261+
case tr @ TypeRef(pre, _) if tr.symbol != NoSymbol =>
227262
val spre = if tpe.hasTrivialPrefix then s.Type.Empty else loop(pre)
228-
val ssym = sym.symbolName
263+
val ssym = tr.symbol.symbolName
229264
s.TypeRef(spre, ssym, Seq.empty)
230265

231-
case TermRef(pre, sym: Symbol) =>
266+
// when TypeRef refers the refinement of RefinedType e.g.
267+
// TypeRef for `foo.B` in `trait T[A] { val foo: { type B = A } = ???; def bar(b: foo.B) = () }` has NoSymbol
268+
case TypeRef(pre, name: Name) =>
269+
val spre = if tpe.hasTrivialPrefix then s.Type.Empty else loop(pre)
270+
val maybeSym = pre.widen.dealias.lookupSym(name)
271+
maybeSym match
272+
case Some(sym) =>
273+
s.TypeRef(spre, sym.symbolName, Seq.empty)
274+
case None => s.Type.Empty
275+
276+
case tr @ TermRef(pre, _) if tr.symbol != NoSymbol =>
232277
val spre = if(tpe.hasTrivialPrefix) s.Type.Empty else loop(pre)
233-
val ssym = sym.symbolName
278+
val ssym = tr.symbol.symbolName
234279
s.SingleType(spre, ssym)
235280

281+
case TermRef(pre, name: Name) =>
282+
val spre = if tpe.hasTrivialPrefix then s.Type.Empty else loop(pre)
283+
val maybeSym = pre.widen.dealias match
284+
case rt: RefinedType =>
285+
refinementSymtab.lookupOrErr(rt, name, rt.typeSymbol)
286+
case _ => None
287+
maybeSym match
288+
case Some(sym) =>
289+
s.SingleType(spre, sym.symbolName)
290+
case None => s.Type.Empty
291+
236292
case ThisType(TypeRef(_, sym: Symbol)) =>
237293
s.ThisType(sym.symbolName)
238294

@@ -276,6 +332,31 @@ class TypeOps:
276332
case ConstantType(const) =>
277333
s.ConstantType(const.toSemanticConst)
278334

335+
case matchType: MatchType =>
336+
val scases = matchType.cases.map { caseType => caseType match {
337+
case lam: HKTypeLambda => // case Array[t] => t
338+
val paramSyms = lam.paramNames.flatMap { paramName =>
339+
val key = (lam, paramName)
340+
paramRefSymtab.get(key)
341+
}.sscope
342+
lam.resType match {
343+
case defn.MatchCase(key, body) =>
344+
s.MatchType.CaseType(
345+
loop(key),
346+
loop(body)
347+
)
348+
case _ => s.MatchType.CaseType() // shouldn't happen
349+
}
350+
case defn.MatchCase(key, body) =>
351+
val skey = loop(key)
352+
val sbody = loop(body)
353+
s.MatchType.CaseType(skey, sbody)
354+
case _ => s.MatchType.CaseType() // shouldn't happen
355+
}}
356+
val sscrutinee = loop(matchType.scrutinee)
357+
val sbound = loop(matchType.bound)
358+
s.MatchType(sscrutinee, scases)
359+
279360
case rt @ RefinedType(parent, name, info) =>
280361
// `X { def x: Int; def y: Int }`
281362
// RefinedType(
@@ -405,8 +486,6 @@ class TypeOps:
405486
// Not yet supported
406487
case _: HKTypeLambda =>
407488
s.Type.Empty
408-
case _: MatchType =>
409-
s.Type.Empty
410489

411490
case tvar: TypeVar =>
412491
loop(tvar.stripped)
@@ -423,8 +502,12 @@ class TypeOps:
423502
tpe match {
424503
case TypeRef(pre, sym: Symbol) =>
425504
checkTrivialPrefix(pre, sym)
505+
case tr @ TypeRef(pre, _) if tr.symbol != NoSymbol =>
506+
checkTrivialPrefix(pre, tr.symbol)
426507
case TermRef(pre, sym: Symbol) =>
427508
checkTrivialPrefix(pre, sym)
509+
case tr @ TermRef(pre, _) if tr.symbol != NoSymbol =>
510+
checkTrivialPrefix(pre, tr.symbol)
428511
case _ => false
429512
}
430513

0 commit comments

Comments
 (0)