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 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ import dotty.tools.dotc.core.Names.Designator
class TypeOps:
import SymbolScopeOps._
import Scala3.given
private val paramRefSymtab = mutable.Map[(LambdaType, Name), Symbol]()
private val refinementSymtab = mutable.Map[(RefinedType, Name), Symbol]()
private val paramRefSymtab = mutable.Map[(LambdaType, Name), SemanticSymbol]()
private val refinementSymtab = mutable.Map[(RefinedType, Name), SemanticSymbol]()

// save generated fake symbols so we can insert them into symbols section of SemanticDB
val fakeSymbols = mutable.Set[FakeSymbol]()
given typeOps: TypeOps = this

extension [T <: LambdaType | RefinedType](symtab: mutable.Map[(T, Name), Symbol])
extension [T <: LambdaType | RefinedType](symtab: mutable.Map[(T, Name), SemanticSymbol])
private def lookup(
binder: T,
name: Name,
)(using Context): Option[Symbol] =
)(using Context): Option[SemanticSymbol] =
symtab.get((binder, name))

extension [T <: LambdaType | RefinedType](symtab: mutable.Map[(T, Name), Symbol])
extension [T <: LambdaType | RefinedType](symtab: mutable.Map[(T, Name), SemanticSymbol])
private def lookupOrErr(
binder: T,
name: Name,
parent: Symbol,
)(using Context): Option[Symbol] =
)(using Context): Option[SemanticSymbol] =
// In case refinement or type param cannot be accessed from traverser and
// no symbols are registered to the symbol table, fall back to Type.member
symtab.lookup(binder, name) match
Expand Down Expand Up @@ -241,11 +241,22 @@ class TypeOps:
// 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) =>
def lookupSym(tpe: Type): Option[SemanticSymbol] = {
tpe match {
case rt: RefinedType =>
refinementSymtab.lookupOrErr(rt, name, rt.typeSymbol)
case rec: RecType =>
lookupSym(rec.parent)
case AndType(tp1, tp2) =>
lookupSym(tp1).orElse(lookupSym(tp2))
case OrType(tp1, tp2) =>
lookupSym(tp1).orElse(lookupSym(tp2))
case _ =>
None
}
}
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
val maybeSym = lookupSym(pre.widen.dealias)
maybeSym match
case Some(sym) =>
s.TypeRef(spre, sym.symbolName, Seq.empty)
Expand Down Expand Up @@ -368,7 +379,9 @@ class TypeOps:

val decls: List[SemanticSymbol] = refinedInfos.map { (name, info) =>
refinementSymtab.lookup(rt, name).getOrElse {
RefinementSymbol(sym, name, info).tap(registerFakeSymbol)
val fakeSym = RefinementSymbol(sym, name, info).tap(registerFakeSymbol)
refinementSymtab((rt, name)) = fakeSym
fakeSym
}
}
val sdecls = decls.sscopeOpt(using LinkMode.HardlinkChildren)
Expand Down
4 changes: 4 additions & 0 deletions tests/semanticdb/expect/Advanced.expect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ object Test/*<-advanced::Test.*/ {
()
}
}

// see: https://github.com/lampepfl/dotty/pull/14608#discussion_r835642563
lazy val foo/*<-advanced::Test.foo.*/: (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local16*/ = Int/*->scala::Int#*/ }) &/*->scala::`&`#*/ (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local17*/ = Int/*->scala::Int#*/; val a/*<-local18*/: A/*->local17*/ }) = ???/*->scala::Predef.`???`().*/
def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A = foo/*->advanced::Test.foo.*/.a
Copy link
Member

Choose a reason for hiding this comment

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

do you have an idea why .A and .a do not have occurrences here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess this is because .A and .a (access the field of refinements), don't have symbols (we're crafting fake symbols for them), and ExtractSemanticDB doesn't resolve the fake symbols by name.

We can see the same problem around here:
https://github.com/lampepfl/dotty/blob/60c336ef34fe0e77851a7652b910db13b84dfe6f/tests/semanticdb/expect/Advanced.expect.scala#L17

I'll take a look 👀

Copy link
Member

@bishabosha bishabosha Mar 30, 2022

Choose a reason for hiding this comment

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

I guess looking again registerUse or registerUseGuarded still only assume real symbols, for example Select tree checks that the symbol exists, without checking for a registered fake symbol - this could be a follow up PR

Copy link
Member Author

Choose a reason for hiding this comment

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

As per foo.A, b964694 as you mentioned, you're right, we could fix the issue by looking up the fake symbols and registerUse them.

In terms of foo.a (field access to structural type), it is transformed to foo.selectDynamic("a") by Typer here ↓
https://github.com/lampepfl/dotty/blob/b9646942facccdcc110753f019c14b941fae870d/compiler/src/dotty/tools/dotc/typer/Dynamic.scala#L49-L65
We have to check if qual is something like Apply(Select(Ident(foo), name), List(Literal(Constant("a")))) if name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed, and lookup symbol for a from foo (This is not yet fixed, can I work on this in another PR?)

Copy link
Member

Choose a reason for hiding this comment

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

I think this is fine for another PR

}


Expand Down
4 changes: 4 additions & 0 deletions tests/semanticdb/expect/Advanced.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ object Test {
()
}
}

// see: https://github.com/lampepfl/dotty/pull/14608#discussion_r835642563
lazy val foo: (reflect.Selectable { type A = Int }) & (reflect.Selectable { type A = Int; val a: A }) = ???
def bar: foo.A = foo.a
}


Expand Down
64 changes: 44 additions & 20 deletions tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ Schema => SemanticDB v4
Uri => Advanced.scala
Text => empty
Language => Scala
Symbols => 55 entries
Occurrences => 114 entries
Synthetics => 3 entries
Symbols => 62 entries
Occurrences => 130 entries
Synthetics => 4 entries

Symbols:
advanced/C# => class C [typeparam T ] extends Object { self: C[T] => +3 decls }
Expand Down Expand Up @@ -80,10 +80,12 @@ advanced/Structural#s2(). => method s2 => Object { abstract val method x Int }
advanced/Structural#s3(). => method s3 => Object { abstract method m (param x: Int): Int }
advanced/Structural#s4(). => method s4 (param a: Int): Object { abstract val method x Int }
advanced/Structural#s4().(a) => param a: Int
advanced/Test. => final object Test extends Object { self: Test.type => +11 decls }
advanced/Test. => final object Test extends Object { self: Test.type => +13 decls }
advanced/Test.bar(). => method bar => foo.A
advanced/Test.e. => val method e Wildcards
advanced/Test.e1. => val method e1 List[_] forSome { type _ }
advanced/Test.e1x. => val method e1x Any
advanced/Test.foo. => lazy val method foo Selectable { abstract type A = Int } & Selectable { abstract field a: local20; abstract type A = Int }
advanced/Test.s. => val method s Structural
advanced/Test.s1. => val method s1 Object { abstract val method x Int }
advanced/Test.s1x. => val method s1x Int
Expand All @@ -108,6 +110,11 @@ local11 => abstract val method x Int
local12 => type B = A
local13 => val local e3: List[local14]
local15 => val local e3x: local14
local16 => type A = Int
local17 => type A = Int
local18 => abstract val method a A
local19 => abstract type A = Int
local21 => abstract field a: A

Occurrences:
[0:8..0:16): advanced <- advanced/
Expand Down Expand Up @@ -208,27 +215,44 @@ Occurrences:
[40:12..40:15): e3x <- local15
[40:18..40:20): e3 -> local13
[40:21..40:25): head -> scala/collection/IterableOps#head().
[48:6..48:13): HKClass <- advanced/HKClass#
[48:14..48:15): F <- advanced/HKClass#[F]
[48:20..48:21): T <- advanced/HKClass#`<init>`().[F][T]
[48:28..48:29): U <- advanced/HKClass#`<init>`().[F][U]
[48:36..48:37): U -> advanced/HKClass#`<init>`().[F][U]
[48:39..48:40): T -> advanced/HKClass#`<init>`().[F][T]
[49:6..49:9): foo <- advanced/HKClass#foo().
[49:10..49:11): T <- advanced/HKClass#foo().[T]
[49:12..49:13): U <- advanced/HKClass#foo().[U]
[49:15..49:16): x <- advanced/HKClass#foo().(x)
[49:18..49:19): F -> advanced/HKClass#[F]
[49:20..49:21): T -> advanced/HKClass#foo().[T]
[49:23..49:24): U -> advanced/HKClass#foo().[U]
[49:28..49:34): String -> scala/Predef.String#
[49:37..49:38): x -> advanced/HKClass#foo().(x)
[49:39..49:47): toString -> scala/Tuple2#toString().
[46:11..46:14): foo <- advanced/Test.foo.
[46:17..46:24): reflect -> scala/reflect/
[46:25..46:35): Selectable -> scala/reflect/Selectable#
[46:43..46:44): A <- local16
[46:47..46:50): Int -> scala/Int#
[46:54..46:55): & -> scala/`&`#
[46:57..46:64): reflect -> scala/reflect/
[46:65..46:75): Selectable -> scala/reflect/Selectable#
[46:83..46:84): A <- local17
[46:87..46:90): Int -> scala/Int#
[46:96..46:97): a <- local18
[46:99..46:100): A -> local17
[46:106..46:109): ??? -> scala/Predef.`???`().
[47:6..47:9): bar <- advanced/Test.bar().
[47:11..47:14): foo -> advanced/Test.foo.
[47:19..47:22): foo -> advanced/Test.foo.
[52:6..52:13): HKClass <- advanced/HKClass#
[52:14..52:15): F <- advanced/HKClass#[F]
[52:20..52:21): T <- advanced/HKClass#`<init>`().[F][T]
[52:28..52:29): U <- advanced/HKClass#`<init>`().[F][U]
[52:36..52:37): U -> advanced/HKClass#`<init>`().[F][U]
[52:39..52:40): T -> advanced/HKClass#`<init>`().[F][T]
[53:6..53:9): foo <- advanced/HKClass#foo().
[53:10..53:11): T <- advanced/HKClass#foo().[T]
[53:12..53:13): U <- advanced/HKClass#foo().[U]
[53:15..53:16): x <- advanced/HKClass#foo().(x)
[53:18..53:19): F -> advanced/HKClass#[F]
[53:20..53:21): T -> advanced/HKClass#foo().[T]
[53:23..53:24): U -> advanced/HKClass#foo().[U]
[53:28..53:34): String -> scala/Predef.String#
[53:37..53:38): x -> advanced/HKClass#foo().(x)
[53:39..53:47): toString -> scala/Tuple2#toString().

Synthetics:
[27:12..27:16):s.s1 => reflectiveSelectable(*)
[29:12..29:16):s.s2 => reflectiveSelectable(*)
[31:12..31:16):s.s3 => reflectiveSelectable(*)
[47:19..47:24):foo.a => *[foo.A]

expect/Annotations.scala
------------------------
Expand Down