Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support MatchType for SemanticDB #14608

Merged
merged 15 commits into from
Apr 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 22 additions & 14 deletions compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,12 @@ class ExtractSemanticDB extends Phase:
val sym = tree.symbol.adjustIfCtorTyparam
if qualSpan.exists && qualSpan.hasLength then
traverse(qual)
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
if (sym != NoSymbol)
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
else
qual.symbol.info.lookupSym(tree.name).foreach(sym =>
registerUseGuarded(qual.symbol.ifExists, sym, selectSpan(tree), tree.source)
)
case tree: Import =>
if tree.span.exists && tree.span.hasLength then
traverseChildren(tree)
Expand Down Expand Up @@ -420,19 +425,22 @@ class ExtractSemanticDB extends Phase:
else
val symkinds = mutable.HashSet.empty[SymbolKind]
tree match
case tree: ValDef =>
if !tree.symbol.is(Param) then
symkinds += (if tree.mods is Mutable then SymbolKind.Var else SymbolKind.Val)
if tree.rhs.isEmpty && !tree.symbol.isOneOf(TermParam | CaseAccessor | ParamAccessor) then
symkinds += SymbolKind.Abstract
case tree: DefDef =>
if tree.isSetterDef then
symkinds += SymbolKind.Setter
else if tree.rhs.isEmpty then
symkinds += SymbolKind.Abstract
case tree: Bind =>
symkinds += SymbolKind.Val
case _ =>
case tree: ValDef =>
if !tree.symbol.is(Param) then
symkinds += (if tree.mods is Mutable then SymbolKind.Var else SymbolKind.Val)
if tree.rhs.isEmpty && !tree.symbol.isOneOf(TermParam | CaseAccessor | ParamAccessor) then
symkinds += SymbolKind.Abstract
case tree: DefDef =>
if tree.isSetterDef then
symkinds += SymbolKind.Setter
else if tree.rhs.isEmpty then
symkinds += SymbolKind.Abstract
// if symbol isType, it's type variable
case tree: Bind if (!tree.symbol.isType) =>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added !tree.symbol.isType here, to misinterpret the type variable symbol as Val
This change makes type variables like local0 => case val method N$1 <: Nat to local0 => case type N$1 <: Nat.

Other diffs: just indented.

symkinds += SymbolKind.Val
case tree: Bind if (tree.symbol.isType) =>
symkinds += SymbolKind.TypeVal
case _ =>
symkinds.toSet

private def ctorParams(
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,12 @@ class SymbolInformationPrinter (symtab: PrinterSymtab):
s"=> ${normal(utpe)}"
case RepeatedType(utpe) =>
s"${normal(utpe)}*"
case _ =>
case MatchType(scrutinee, cases) =>
val casesStr = cases.map { caseType =>
s"${pprint(caseType.key)} => ${pprint(caseType.body)}"
}.mkString(", ")
s"${pprint(scrutinee)} match { ${casesStr} }"
case x =>
"<?>"

def normal(tpe: Type): String = tpe match
Expand Down
31 changes: 21 additions & 10 deletions compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import core.StdNames.nme
import SymbolInformation.{Kind => k}
import dotty.tools.dotc.util.SourceFile
import dotty.tools.dotc.util.Spans.Span
import dotty.tools.dotc.core.Names.Designator

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

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

def namePresentInSource(sym: Symbol, span: Span, source:SourceFile)(using Context): Boolean =
def namePresentInSource(desig: Designator, span: Span, source:SourceFile)(using Context): Boolean =
if !span.exists then false
else
val content = source.content()
val (start, end) =
if content.lift(span.end - 1).exists(_ == '`') then
(span.start + 1, span.end - 1)
else (span.start, span.end)
// println(s"${start}, $end")
val nameInSource = content.slice(start, end).mkString
// for secondary constructors `this`
if sym.isConstructor && nameInSource == nme.THISkw.toString then
true
else
val target =
if sym.isPackageObject then sym.owner
else sym
nameInSource == target.name.stripModuleClassSuffix.lastPart.toString
desig match
case sym: Symbol =>
if sym.isConstructor && nameInSource == nme.THISkw.toString then
true
else
val target =
if sym.isPackageObject then sym.owner
else sym
nameInSource == target.name.stripModuleClassSuffix.lastPart.toString
case name: Name =>
// println(nameInSource)
// println(name.mangledString)
nameInSource == name.mangledString

sealed trait FakeSymbol {
private[Scala3] var sname: Option[String] = None
Expand Down Expand Up @@ -153,12 +161,13 @@ object Scala3:
enum SymbolKind derives CanEqual:
kind =>

case Val, Var, Setter, Abstract
case Val, Var, Setter, Abstract, TypeVal

def isVar: Boolean = kind match
case Var | Setter => true
case _ => false
def isVal: Boolean = kind == Val
def isTypeVal: Boolean = kind == TypeVal
def isVarOrVal: Boolean = kind.isVar || kind.isVal

end SymbolKind
Expand Down Expand Up @@ -309,7 +318,9 @@ object Scala3:
props |= SymbolInformation.Property.IMPLICIT.value
if sym.is(Lazy, butNot=Module) then
props |= SymbolInformation.Property.LAZY.value
if sym.isAllOf(Case | Module) || sym.is(CaseClass) || sym.isAllOf(EnumCase) then
if sym.isAllOf(Case | Module) ||
(sym.is(CaseClass) && !symkinds.exists(_.isTypeVal)) || // `t` of `case List[t] =>` (which has `CaseClass` flag) shouldn't be `CASE`
sym.isAllOf(EnumCase) then
props |= SymbolInformation.Property.CASE.value
if sym.is(Covariant) then
props |= SymbolInformation.Property.COVARIANT.value
Expand Down
97 changes: 90 additions & 7 deletions compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import collection.mutable

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

class TypeOps:
import SymbolScopeOps._
Expand All @@ -33,7 +34,7 @@ class TypeOps:
)(using Context): Option[Symbol] =
symtab.get((binder, name))

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

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

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

extension (tpe: Type)
def lookupSym(name: Name)(using Context): Option[Symbol] = {
def loop(ty: Type): Option[Symbol] = ty match
case rt: RefinedType =>
refinementSymtab.lookup(rt, name).orElse(
loop(rt.parent)
)
case rec: RecType =>
loop(rec.parent)
case AndType(tp1, tp2) =>
loop(tp1).orElse(loop(tp2))
case OrType(tp1, tp2) =>
loop(tp1).orElse(loop(tp2))
case _ =>
None
loop(tpe.dealias)
}

def toSemanticSig(using LinkMode, Context, SemanticSymbolBuilder)(sym: Symbol): s.Signature =
def enterParamRef(tpe: Type): Unit =
tpe match {
Expand All @@ -81,6 +101,10 @@ class TypeOps:
else
enterParamRef(lam.resType)

// for CaseType `case Array[t] => t` which is represented as [t] =>> MatchCase[Array[t], t]
case m: MatchType =>
m.cases.foreach(enterParamRef)

// for class constructor
// class C[T] { ... }
case cls: ClassInfo if sym.info.isInstanceOf[LambdaType] =>
Expand Down Expand Up @@ -137,6 +161,12 @@ class TypeOps:
enterRefined(expr.resType)
case m: LambdaType =>
enterRefined(m.resType)
case AndType(t1, t2) =>
enterRefined(t1)
enterRefined(t2)
case OrType(t1, t2) =>
enterRefined(t1)
enterRefined(t2)
case _ => ()
}
if sym.exists && sym.owner.exists then
Expand Down Expand Up @@ -223,16 +253,42 @@ class TypeOps:
val stpe = loop(tpe)
s.ByNameType(stpe)

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

case TermRef(pre, sym: Symbol) =>
// when TypeRef refers the refinement of RefinedType e.g.
// TypeRef for `foo.B` in `trait T[A] { val foo: { type B = A } = ???; def bar(b: foo.B) = () }` has NoSymbol
case TypeRef(pre, name: Name) =>
val spre = if tpe.hasTrivialPrefix then s.Type.Empty else loop(pre)
val maybeSym = pre.widen.dealias.lookupSym(name)
maybeSym match
case Some(sym) =>
s.TypeRef(spre, sym.symbolName, Seq.empty)
case None => s.Type.Empty

case tr @ TermRef(pre, _) if tr.symbol != NoSymbol =>
val spre = if(tpe.hasTrivialPrefix) s.Type.Empty else loop(pre)
val ssym = sym.symbolName
val ssym = tr.symbol.symbolName
s.SingleType(spre, ssym)

case TermRef(pre, name: Name) =>
val spre = if tpe.hasTrivialPrefix then s.Type.Empty else loop(pre)
val maybeSym = pre.widen.dealias match
case rt: RefinedType =>
refinementSymtab.lookupOrErr(rt, name, rt.typeSymbol)
case _ => None
maybeSym match
case Some(sym) =>
s.SingleType(spre, sym.symbolName)
case None => s.Type.Empty

case ThisType(TypeRef(_, sym: Symbol)) =>
s.ThisType(sym.symbolName)

Expand Down Expand Up @@ -276,6 +332,31 @@ class TypeOps:
case ConstantType(const) =>
s.ConstantType(const.toSemanticConst)

case matchType: MatchType =>
val scases = matchType.cases.map { caseType => caseType match {
case lam: HKTypeLambda => // case Array[t] => t
val paramSyms = lam.paramNames.flatMap { paramName =>
val key = (lam, paramName)
paramRefSymtab.get(key)
}.sscope
lam.resType match {
case defn.MatchCase(key, body) =>
s.MatchType.CaseType(
loop(key),
loop(body)
)
case _ => s.MatchType.CaseType() // shouldn't happen
}
case defn.MatchCase(key, body) =>
val skey = loop(key)
val sbody = loop(body)
s.MatchType.CaseType(skey, sbody)
case _ => s.MatchType.CaseType() // shouldn't happen
}}
val sscrutinee = loop(matchType.scrutinee)
val sbound = loop(matchType.bound)
s.MatchType(sscrutinee, scases)

case rt @ RefinedType(parent, name, info) =>
// `X { def x: Int; def y: Int }`
// RefinedType(
Expand Down Expand Up @@ -405,8 +486,6 @@ class TypeOps:
// Not yet supported
case _: HKTypeLambda =>
s.Type.Empty
case _: MatchType =>
s.Type.Empty

case tvar: TypeVar =>
loop(tvar.stripped)
Expand All @@ -423,8 +502,12 @@ class TypeOps:
tpe match {
case TypeRef(pre, sym: Symbol) =>
checkTrivialPrefix(pre, sym)
case tr @ TypeRef(pre, _) if tr.symbol != NoSymbol =>
checkTrivialPrefix(pre, tr.symbol)
case TermRef(pre, sym: Symbol) =>
checkTrivialPrefix(pre, sym)
case tr @ TermRef(pre, _) if tr.symbol != NoSymbol =>
checkTrivialPrefix(pre, tr.symbol)
case _ => false
}

Expand Down