Skip to content

Commit 9cfb58a

Browse files
committed
Fix missing semantic tokens for named tuple fields
1 parent 122fd6a commit 9cfb58a

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import dotty.tools.dotc.ast.tpd.*
1414
import dotty.tools.dotc.ast.untpd
1515
import dotty.tools.dotc.ast.untpd.ExtMethods
1616
import dotty.tools.dotc.ast.untpd.ImportSelector
17+
import dotty.tools.dotc.core.Constants.Constant
1718
import dotty.tools.dotc.core.Contexts.*
1819
import dotty.tools.dotc.core.Flags
1920
import dotty.tools.dotc.core.NameOps.*
@@ -183,6 +184,41 @@ trait PcCollector[T]:
183184
traverser(set, tree)
184185
}
185186

187+
/** Named tuple field access such as:
188+
* ```
189+
* val x: (name: String) = ???
190+
* x.<<name>>
191+
* ```
192+
* Named tuple selections are desugared to `NamedTuple.apply[N, V](qual)(idx)`,
193+
*/
194+
case app @ Apply(
195+
Apply(TypeApply(fun, List(t1, t2)), List(qual)),
196+
List(Literal(Constant(i: Int)))
197+
)
198+
if fun.symbol.exists && fun.symbol.name == nme.apply
199+
&& fun.symbol.owner.exists
200+
&& fun.symbol.owner == defn.NamedTupleModule.moduleClass
201+
&& app.span.isCorrect =>
202+
def getIndex(t: Tree): Option[Type] =
203+
t.tpe.dealias match
204+
case AppliedType(_, args) => args.get(i)
205+
case _ => None
206+
val fieldNameOpt = getIndex(t1) match
207+
case Some(c: ConstantType) => Some(termName(c.value.stringValue))
208+
case _ => None
209+
val fieldType = getIndex(t2).getOrElse(NoType)
210+
val fieldOccurrence = fieldNameOpt match
211+
case Some(fieldName) =>
212+
val fieldSpan = Span(app.span.point, app.span.end, app.span.point)
213+
if fieldSpan.isCorrect then
214+
val sym = newSymbol(NoSymbol, fieldName: Name, Flags.EmptyFlags, fieldType)
215+
Set(collect(app, pos.withSpan(fieldSpan), Some(sym)))
216+
else Set.empty
217+
case None => Set.empty
218+
val traverser =
219+
new PcCollector.DeepFolderWithParent[Set[T]](collectNamesWithParent)
220+
traverser(occurrences ++ fieldOccurrence, qual)
221+
186222
/* Named parameters don't have symbol so we need to check the owner
187223
* ```
188224
* foo(<<name>> = "abc")

presentation-compiler/test/dotty/tools/pc/tests/tokens/SemanticTokensSuite.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,19 @@ class SemanticTokensSuite extends BaseSemanticTokensSuite:
469469
|export <<Test>>/*class*/.<<methodA>>/*method*/
470470
|""".stripMargin
471471
)
472+
473+
@Test def `named-tuple-members` =
474+
check(
475+
"""|object <<A>>/*class*/:
476+
| type <<PersonNN>>/*type,definition*/ = (name: <<String>>/*type*/, age: <<Int>>/*class,abstract*/)
477+
| final case class <<PersonCC>>/*class*/(<<name>>/*variable,declaration,readonly*/: <<String>>/*type*/, <<age>>/*variable,declaration,readonly*/: <<Int>>/*class,abstract*/)
478+
|
479+
| val <<pNN>>/*variable,definition,readonly*/: <<PersonNN>>/*type*/ = (name = "John", age = 30)
480+
| val <<pCC>>/*variable,definition,readonly*/: <<PersonCC>>/*class*/ = <<PersonCC>>/*class*/(<<name>>/*parameter,readonly*/ = "John", <<age>>/*parameter,readonly*/ = 30)
481+
|
482+
| val <<nn>>/*variable,definition,readonly*/ = <<pNN>>/*variable,readonly*/.<<name>>/*variable,readonly*/
483+
| val <<cc>>/*variable,definition,readonly*/ = <<pCC>>/*variable,readonly*/.<<name>>/*variable,readonly*/
484+
| val <<ccAge>>/*variable,definition,readonly*/ = <<pNN>>/*variable,readonly*/.<<age>>/*variable,readonly*/
485+
| val <<ccAge>>/*variable,definition,readonly*/ = <<pCC>>/*variable,readonly*/.<<age>>/*variable,readonly*/
486+
|""".stripMargin
487+
)

0 commit comments

Comments
 (0)