dotty.tools.dotc.semanticdb.ExtractSemanticDB.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala3-compiler_3 Show documentation
Show all versions of scala3-compiler_3 Show documentation
scala3-compiler-bootstrapped
package dotty.tools
package dotc
package semanticdb
import scala.language.unsafeNulls
import core.*
import Phases.*
import ast.tpd.*
import ast.Trees.{mods, WithEndMarker}
import Contexts.*
import Symbols.*
import Flags.*
import Names.Name
import StdNames.nme
import NameOps.*
import Denotations.StaleSymbol
import util.Spans.Span
import util.SourceFile
import scala.collection.mutable
import scala.annotation.{ threadUnsafe => tu, tailrec }
import scala.jdk.CollectionConverters.*
import scala.PartialFunction.condOpt
import typer.ImportInfo.withRootImports
import dotty.tools.dotc.reporting.Diagnostic.Warning
import dotty.tools.dotc.{semanticdb => s}
import dotty.tools.io.{AbstractFile, JarArchive}
import dotty.tools.dotc.semanticdb.DiagnosticOps.*
import scala.util.{Using, Failure, Success}
import java.nio.file.Path
/** Extract symbol references and uses to semanticdb files.
* See https://scalameta.org/docs/semanticdb/specification.html#symbol-1
* for a description of the format.
*
* Here, we define two phases for "ExtractSemanticDB", "PostTyper" and "PostInlining".
*
* The "PostTyper" phase extracts SemanticDB information such as symbol
* definitions, symbol occurrences, type information, and synthetics
* and write .semanticdb file.
*
* The "PostInlining" phase extracts diagnostics from "ctx.reporter" and
* attaches them to the SemanticDB information extracted in the "PostTyper" phase.
* We need to run this phase after the "CheckUnused.PostInlining" phase
* so that we can extract the warnings generated by "-Wunused".
*/
class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends Phase:
override val phaseName: String = ExtractSemanticDB.phaseNamePrefix + phaseMode.toString()
override val description: String = ExtractSemanticDB.description
override def isRunnable(using Context) =
import ExtractSemanticDB.{semanticdbTarget, outputDirectory}
def writesToOutputJar = semanticdbTarget.isEmpty && outputDirectory.isInstanceOf[JarArchive]
(super.isRunnable || ctx.isBestEffort) && ctx.settings.Xsemanticdb.value && !writesToOutputJar
// Check not needed since it does not transform trees
override def isCheckable: Boolean = false
private def computeDiagnostics(
sourceRoot: String,
warnings: Map[SourceFile, List[Warning]],
append: ((Path, List[Diagnostic])) => Unit)(using Context): Boolean = monitor(phaseName) {
val unit = ctx.compilationUnit
warnings.get(unit.source).foreach { ws =>
val outputDir =
ExtractSemanticDB.semanticdbPath(
unit.source,
ExtractSemanticDB.semanticdbOutDir,
sourceRoot
)
append((outputDir, ws.map(_.toSemanticDiagnostic)))
}
}
private def extractSemanticDB(sourceRoot: String, writeSemanticdbText: Boolean)(using Context): Boolean =
monitor(phaseName) {
val unit = ctx.compilationUnit
val outputDir =
ExtractSemanticDB.semanticdbPath(
unit.source,
ExtractSemanticDB.semanticdbOutDir,
sourceRoot
)
val extractor = ExtractSemanticDB.Extractor()
extractor.extract(unit.tpdTree)
ExtractSemanticDB.write(
unit.source,
extractor.occurrences.toList,
extractor.symbolInfos.toList,
extractor.synthetics.toList,
outputDir,
sourceRoot,
writeSemanticdbText
)
}
override def runOn(units: List[CompilationUnit])(using ctx: Context): List[CompilationUnit] = {
val sourceRoot = ctx.settings.sourceroot.value
val appendDiagnostics = phaseMode == ExtractSemanticDB.PhaseMode.AppendDiagnostics
val unitContexts = units.map(ctx.fresh.setCompilationUnit(_).withRootImports)
if (appendDiagnostics)
val warnings = ctx.reporter.allWarnings.groupBy(w => w.pos.source)
val buf = mutable.ListBuffer.empty[(Path, Seq[Diagnostic])]
val units0 =
for unitCtx <- unitContexts if computeDiagnostics(sourceRoot, warnings, buf += _)(using unitCtx)
yield unitCtx.compilationUnit
cancellable {
buf.toList.asJava.parallelStream().forEach { case (out, warnings) =>
ExtractSemanticDB.appendDiagnostics(warnings, out)
}
}
units0
else
val writeSemanticdbText = ctx.settings.semanticdbText.value
for unitCtx <- unitContexts if extractSemanticDB(sourceRoot, writeSemanticdbText)(using unitCtx)
yield unitCtx.compilationUnit
}
def run(using Context): Unit = unsupported("run")
end ExtractSemanticDB
object ExtractSemanticDB:
import java.nio.file.Path
import java.nio.file.Files
import java.nio.file.Paths
val phaseNamePrefix: String = "extractSemanticDB"
val description: String = "extract info into .semanticdb files"
enum PhaseMode:
case ExtractSemanticInfo
case AppendDiagnostics
class ExtractSemanticInfo extends ExtractSemanticDB(PhaseMode.ExtractSemanticInfo)
class AppendDiagnostics extends ExtractSemanticDB(PhaseMode.AppendDiagnostics)
private def semanticdbTarget(using Context): Option[Path] =
Option(ctx.settings.semanticdbTarget.value)
.filterNot(_.isEmpty)
.map(Paths.get(_))
/** Destination for generated classfiles */
private def outputDirectory(using Context): AbstractFile =
ctx.settings.outputDir.value
/** Output directory for SemanticDB files */
private def semanticdbOutDir(using Context): Path =
semanticdbTarget.getOrElse(outputDirectory.jpath)
private def absolutePath(path: Path): Path = path.toAbsolutePath.normalize
private def write(
source: SourceFile,
occurrences: List[SymbolOccurrence],
symbolInfos: List[SymbolInformation],
synthetics: List[Synthetic],
outpath: Path,
sourceRoot: String,
semanticdbText: Boolean
): Unit =
Files.createDirectories(outpath.getParent())
val doc: TextDocument = TextDocument(
schema = Schema.SEMANTICDB4,
language = Language.SCALA,
uri = Tools.mkURIstring(Paths.get(relPath(source, sourceRoot))),
text = if semanticdbText then String(source.content) else "",
md5 = internal.MD5.compute(String(source.content)),
symbols = symbolInfos,
occurrences = occurrences,
synthetics = synthetics,
)
val docs = TextDocuments(List(doc))
val out = Files.newOutputStream(outpath)
try
val stream = internal.SemanticdbOutputStream.newInstance(out)
docs.writeTo(stream)
stream.flush()
finally
out.close()
end write
private def appendDiagnostics(
diagnostics: Seq[Diagnostic],
outpath: Path
): Unit =
Using.Manager { use =>
val in = use(Files.newInputStream(outpath))
val sin = internal.SemanticdbInputStream.newInstance(in)
val docs = TextDocuments.parseFrom(sin)
val out = use(Files.newOutputStream(outpath))
val sout = internal.SemanticdbOutputStream.newInstance(out)
TextDocuments(docs.documents.map(_.withDiagnostics(diagnostics))).writeTo(sout)
sout.flush()
} match
case Failure(ex) => // failed somehow, should we say something?
case Success(_) => // success to update semanticdb, say nothing
end appendDiagnostics
private def relPath(source: SourceFile, sourceRoot: String) =
SourceFile.relativePath(source, sourceRoot)
private def semanticdbPath(source: SourceFile, base: Path, sourceRoot: String): Path =
absolutePath(base)
.resolve("META-INF")
.resolve("semanticdb")
.resolve(relPath(source, sourceRoot))
.resolveSibling(source.name + ".semanticdb")
/** Extractor of symbol occurrences from trees */
class Extractor extends TreeTraverser:
import Scala3.{_, given}
given s.SemanticSymbolBuilder = s.SemanticSymbolBuilder()
val synth = SyntheticsExtractor()
given converter: s.TypeOps = s.TypeOps()
/** The bodies of synthetic locals */
private val localBodies = mutable.HashMap[Symbol, Tree]()
/** The extracted symbol occurrences */
val occurrences = new mutable.ListBuffer[SymbolOccurrence]()
/** The extracted symbol infos */
val symbolInfos = new mutable.ListBuffer[SymbolInformation]()
val synthetics = new mutable.ListBuffer[s.Synthetic]()
/** A cache of localN names */
val localNames = new mutable.HashSet[String]()
/** The symbol occurrences generated so far, as a set */
private val generated = new mutable.HashSet[SymbolOccurrence]
def extract(tree: Tree)(using Context): Unit =
traverse(tree)
val fakeSyms = converter.fakeSymbols.map(_.symbolInfo(Set.empty)(using LinkMode.SymlinkChildren, converter))
symbolInfos.appendAll(fakeSyms)
/** Definitions of this symbol should be excluded from semanticdb */
private def excludeDef(sym: Symbol)(using Context): Boolean =
!sym.exists
|| sym.isLocalDummy
// basically do not register synthetic symbols, except anonymous class
// `new Foo { ... }`
|| (sym.is(Synthetic) && !sym.isAnonymousClass)
|| sym.isSetter
|| sym.isOldStyleImplicitConversion(forImplicitClassOnly = true)
|| sym.owner.isGivenInstanceSummoner
|| excludeDefOrUse(sym)
private def excludeDefOrUse(sym: Symbol)(using Context): Boolean =
!sym.exists
|| sym.name.is(NameKinds.DefaultGetterName)
|| sym.isConstructor && (sym.owner.is(ModuleClass) || !sym.isGlobal)
|| excludeSymbol(sym)
private def excludeSymbol(sym: Symbol)(using Context): Boolean =
!sym.exists
|| sym.is(ConstructorProxy)
|| sym.name.isWildcard
|| excludeQual(sym)
private def excludeQual(sym: Symbol)(using Context): Boolean =
!sym.exists
|| sym.isAnonymousFunction
|| sym.isAnonymousModuleVal
|| sym.name.isEmptyNumbered
private def excludeChildren(sym: Symbol)(using Context): Boolean =
!sym.exists
|| sym.is(Param) && sym.info.bounds.hi.isInstanceOf[Types.HKTypeLambda]
|| sym.isOldStyleImplicitConversion(forImplicitClassOnly = true)
/** Uses of this symbol where the reference has given span should be excluded from semanticdb */
private def excludeUse(qualifier: Option[Symbol], sym: Symbol)(using Context): Boolean =
!sym.exists
|| excludeDefOrUse(sym)
|| sym.isConstructor && sym.owner.isAnnotation
|| sym == defn.Any_typeCast
|| sym.owner == defn.OpsPackageClass
|| qualifier.exists(excludeQual)
/** This block is created by lifting i.e. EtaExpansion */
private def isProbablyLifted(block: Block)(using Context) =
def isSyntheticDef(t: Tree) =
t match
case t: (ValDef | DefDef) => t.symbol.isSyntheticWithIdent
case _ => false
block.stats.forall(isSyntheticDef)
private def traverseAnnotsOfDefinition(sym: Symbol)(using Context): Unit =
for annot <- sym.annotations do
if annot.tree.span.exists
&& annot.tree.span.hasLength then
annot.tree match
case tree: Typed => () // hack for inline code
case tree => traverse(tree)
override def traverse(tree: Tree)(using Context): Unit =
tree match
case tree: DefTree if tree.symbol.exists =>
traverseAnnotsOfDefinition(tree.symbol)
case _ =>
()
tree match
case tree: PackageDef =>
tree.stats.foreach(traverse)
if !excludeDef(tree.pid.symbol) && tree.pid.span.hasLength then
tree.pid match
case tree: Select =>
traverse(tree.qualifier)
registerDefinition(tree.symbol, selectSpan(tree), Set.empty, tree.source)
case tree => registerDefinition(tree.symbol, tree.span, Set.empty, tree.source)
case tree: NamedDefTree =>
if !tree.symbol.isAllOf(ModuleValCreationFlags) then
tree match {
case tree: ValDef if tree.symbol.isAllOf(EnumValue) =>
tree.rhs match
case Block(TypeDef(_, template: Template) :: _, _) => // simple case with specialised extends clause
template.parents.filter(!_.span.isZeroExtent).foreach(traverse)
case _ => // calls $new
case tree: ValDef if tree.symbol.isSelfSym =>
if tree.tpt.span.hasLength then
traverse(tree.tpt)
case tree: DefDef if tree.symbol.isConstructor => // ignore typeparams for secondary ctors
tree.trailingParamss.foreach(_.foreach(traverse))
traverse(tree.rhs)
case tree: (DefDef | ValDef) if tree.symbol.isSyntheticWithIdent =>
tree match
case tree: DefDef =>
tree.paramss.foreach(_.foreach(param => registerSymbolSimple(param.symbol)))
case tree: ValDef if tree.symbol.is(Given) =>
traverse(tree.tpt)
case _ =>
if !tree.symbol.isGlobal then
localBodies(tree.symbol) = tree.rhs
// ignore rhs
case PatternValDef(pat, rhs) =>
traverse(rhs)
PatternValDef.collectPats(pat).foreach(traverse)
case tree: TypeDef =>
traverseChildren(tree)
case tree =>
if !excludeChildren(tree.symbol) then
traverseChildren(tree)
}
if !excludeDef(tree.symbol) && (tree.span.hasLength || tree.symbol.isAnonymousClass) then
registerDefinition(tree.symbol, tree.nameSpan, symbolKinds(tree), tree.source)
val privateWithin = tree.symbol.privateWithin
if privateWithin.exists then
registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span, tree.source), tree.source)
else if !excludeSymbol(tree.symbol) then
registerSymbol(tree.symbol, symbolKinds(tree))
case tree: Template if tree.symbol != NoSymbol && tree.symbol.owner.is(Invisible) =>
// do nothing
// exclude the symbols and synthetics generated by @main annotation
// (main class generated by @main has `Invisible` flag, see `MainProxies.scala`).
case tree: Template =>
val ctorSym = tree.constr.symbol
for parent <- tree.parentsOrDerived if parent.span.hasLength do
traverse(parent)
val selfSpan = tree.self.span
if selfSpan.exists && selfSpan.hasLength then
traverse(tree.self)
if tree.symbol != NoSymbol && tree.symbol.owner.isEnumClass then
tree.body.foreachUntilImport(traverse).foreach(traverse) // the first import statement
else
tree.body.foreach(traverse)
if !excludeDef(ctorSym) then
traverseAnnotsOfDefinition(ctorSym)
ctorParams(tree.constr.termParamss, tree.constr.leadingTypeParams, tree.body)
registerDefinition(ctorSym, tree.constr.nameSpan.startPos, Set.empty, tree.source)
case tree: Apply =>
@tu lazy val genParamSymbol: Name => String = tree.fun.symbol.funParamSymbol
traverse(tree.fun)
synth.tryFindSynthetic(tree).foreach(synthetics.addOne)
for arg <- tree.args do
arg match
case tree @ NamedArg(name, arg) =>
traverse(localBodies.get(arg.symbol).getOrElse(arg))
registerUse(genParamSymbol(name), tree.span.startPos.withEnd(tree.span.start + name.toString.length), tree.source)
case _ => traverse(arg)
case tree: Assign =>
val qualSym = condOpt(tree.lhs) { case Select(qual, _) if qual.symbol.exists => qual.symbol }
if !excludeUse(qualSym, tree.lhs.symbol) then
val lhs = tree.lhs.symbol
val setter = lhs.matchingSetter.orElse(lhs)
tree.lhs match
case tree: Select => registerUse(setter, selectSpan(tree), tree.source)
case tree => registerUse(setter, tree.span, tree.source)
traverseChildren(tree.lhs)
traverse(tree.rhs)
case tree: Ident =>
if tree.name != nme.WILDCARD then
val sym = tree.symbol.adjustIfCtorTyparam
registerUseGuarded(None, sym, tree.span, tree.source)
case tree: Select =>
val qual = tree.qualifier
val qualSpan = qual.span
val sym = tree.symbol.adjustIfCtorTyparam
if qualSpan.exists && qualSpan.hasLength then
traverse(qual)
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)
for sel <- tree.selectors do
val imported = sel.imported.name
if imported != nme.WILDCARD then
for alt <- tree.expr.tpe.member(imported).alternatives do
registerUseGuarded(None, alt.symbol, sel.imported.span, tree.source)
try
if (alt.symbol.companionClass.exists)
registerUseGuarded(None, alt.symbol.companionClass, sel.imported.span, tree.source)
catch case ex: StaleSymbol =>
// can happen for constructor proxies. Test case is pos-macros/i13532.
()
case tree: Inlined =>
traverse(tree.call)
case tree: TypeApply =>
synth.tryFindSynthetic(tree).foreach(synthetics.addOne)
traverseChildren(tree)
case tree: TypeTree =>
tree.typeOpt match
// Any types could be appear inside of `TypeTree`, but
// types that precent in source other than TypeRef are traversable and contain Ident tree nodes
// (e.g. TypeBoundsTree, AppliedTypeTree)
case Types.TypeRef(_, sym: Symbol) if namePresentInSource(sym, tree.span, tree.source) =>
registerUseGuarded(None, sym, tree.span, tree.source)
case _ => ()
// If tree is lifted, ignore Synthetic status on all the definitions and traverse all childrens
case tree: Block if isProbablyLifted(tree) =>
tree.stats.foreach:
case t: (ValDef | DefDef) if !excludeChildren(t.symbol) => traverseChildren(t)
case _ => ()
traverse(tree.expr)
case _ =>
traverseChildren(tree)
tree match
case tree: WithEndMarker[t] =>
val endSpan = tree.endSpan
if endSpan.exists &&
namePresentInSource(tree.symbol, endSpan, tree.source) then
// non-symbol end marker shouldn't have Symbol Occurrence
registerUseGuarded(None, tree.symbol, endSpan, tree.source)
case _ =>
end traverse
private object PatternValDef:
def unapply(tree: ValDef)(using Context): Option[(Tree, Tree)] = tree.rhs match
case Match(Typed(selected: Tree, tpt: TypeTree), CaseDef(pat: Tree, _, _) :: Nil)
if tpt.span.exists && !tpt.span.hasLength && tpt.tpe.isAnnotatedByUncheckedOrRuntimeChecked =>
Some((pat, selected))
case _ => None
extension (tpe: Types.Type)
private inline def isAnnotatedByUncheckedOrRuntimeChecked(using Context) = tpe match
case Types.AnnotatedType(_, annot) =>
annot.symbol == defn.UncheckedAnnot || annot.symbol == defn.RuntimeCheckedAnnot
case _ => false
def collectPats(pat: Tree): List[Tree] =
@tailrec
def impl(acc: List[Tree], pats: List[Tree]): List[Tree] = pats match
case pat::pats => pat match
case Typed(UnApply(fun: Tree, _, args), tpt: Tree) => impl(fun::tpt::acc, args:::pats)
case Typed(obj: Ident, tpt: Tree) => impl(obj::tpt::acc, pats)
case UnApply(fun: Tree, _, args) => impl(fun::acc, args:::pats)
case obj: Ident => impl(obj::acc, pats)
case _ => impl(acc, pats)
case Nil => acc
impl(Nil, pat::Nil)
end PatternValDef
private def registerSymbol(sym: Symbol, symkinds: Set[SymbolKind])(using Context): Unit =
val sname = sym.symbolName
val isLocal = sname.isLocal
if !isLocal || !localNames.contains(sname) then
if isLocal then
localNames += sname
symbolInfos += sym.symbolInfo(symkinds)(using LinkMode.SymlinkChildren, converter)
private def registerSymbolSimple(sym: Symbol)(using Context): Unit =
registerSymbol(sym, Set.empty)
private def registerOccurrence(symbol: String, span: Span, role: SymbolOccurrence.Role, treeSource: SourceFile)(using Context): Unit =
val occ = SymbolOccurrence(range(span, treeSource), symbol, role)
if !generated.contains(occ) && occ.symbol.nonEmpty then
occurrences += occ
generated += occ
private def registerUseGuarded(qualSym: Option[Symbol], sym: Symbol, span: Span, treeSource: SourceFile)(using Context) =
if !excludeUse(qualSym, sym) && !span.isZeroExtent then
registerUse(sym, span, treeSource)
private def registerUse(sym: Symbol, span: Span, treeSource: SourceFile)(using Context): Unit =
registerUse(sym.symbolName, span, treeSource)
private def registerUse(symbol: String, span: Span, treeSource: SourceFile)(using Context): Unit =
registerOccurrence(symbol, span, SymbolOccurrence.Role.REFERENCE, treeSource)
private def registerDefinition(sym: Symbol, span: Span, symkinds: Set[SymbolKind], treeSource: SourceFile)(using Context) =
val sname = sym.symbolName
val finalSpan = if !span.hasLength || !sym.is(Given) || namePresentInSource(sym, span, treeSource) then
span
else
Span(span.start)
if namePresentInSource(sym, span, treeSource) || sym.isAnonymousClass then
registerOccurrence(sname, finalSpan, SymbolOccurrence.Role.DEFINITION, treeSource)
if !sym.is(Package) then
registerSymbol(sym, symkinds)
private def spanOfSymbol(sym: Symbol, span: Span, treeSource: SourceFile)(using Context): Span =
val contents = if treeSource.exists then treeSource.content() else Array.empty[Char]
val idx = contents.indexOfSlice(sym.name.show, span.start)
val start = if idx >= 0 then idx else span.start
Span(start, start + sym.name.show.length, start)
extension (list: List[List[ValDef]])
private inline def isSingleArg = list match
case (_::Nil)::Nil => true
case _ => false
extension (tree: DefDef)
private def isSetterDef(using Context): Boolean =
tree.name.isSetterName && tree.mods.is(Accessor) && tree.termParamss.isSingleArg
private def findGetters(ctorParams: Set[Names.TermName], body: List[Tree])(using Context): Map[Names.TermName, ValDef] =
if ctorParams.isEmpty || body.isEmpty then
Map.empty
else
body.collect({
case tree: ValDef
if ctorParams.contains(tree.name)
&& !tree.symbol.isPrivate =>
tree.name -> tree
}).toMap
end findGetters
private def selectSpan(tree: Select)(using Context) =
val end = tree.span.end
val limit = tree.qualifier.span.end
if limit < end then
tree.nameSpan
else Span(limit, end)
extension (span: Span)
private def hasLength: Boolean = span.exists && !span.isZeroExtent
/**Consume head while not an import statement.
* Returns the rest of the list after the first import, or else the empty list
*/
extension (body: List[Tree])
@tailrec private def foreachUntilImport(op: Tree => Unit): List[Tree] = body match
case ((_: Import) :: rest) => rest
case stat :: rest =>
op(stat)
rest.foreachUntilImport(op)
case Nil => Nil
extension (sym: Symbol)
private def adjustIfCtorTyparam(using Context) =
if sym.isType && sym.owner.exists && sym.owner.isConstructor then
matchingMemberType(sym, sym.owner.owner)
else
sym
private inline def matchingMemberType(ctorTypeParam: Symbol, classSym: Symbol)(using Context) =
classSym.info.member(ctorTypeParam.name).symbol
/**Necessary because not all of the eventual flags are propagated from the Tree to the symbol yet.
*/
private def symbolKinds(tree: NamedDefTree)(using Context): Set[SymbolKind] =
if tree.symbol.isSelfSym then
Set.empty
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
// if symbol isType, it's type variable
case tree: Bind if (!tree.symbol.isType) =>
symkinds += SymbolKind.Val
case tree: Bind if (tree.symbol.isType) =>
symkinds += SymbolKind.TypeVal
case _ =>
symkinds.toSet
private def ctorParams(
vparamss: List[List[ValDef]], tparams: List[TypeDef], body: List[Tree])(using Context): Unit =
@tu lazy val getters = findGetters(vparamss.flatMap(_.map(_.name)).toSet, body)
for
vparams <- vparamss
vparam <- vparams
do
traverse(vparam.tpt)
if !excludeSymbol(vparam.symbol) then
traverseAnnotsOfDefinition(vparam.symbol)
val symkinds =
getters.get(vparam.name).fold(SymbolKind.emptySet)(getter =>
if getter.mods.is(Mutable) then SymbolKind.VarSet else SymbolKind.ValSet)
registerSymbol(vparam.symbol, symkinds)
traverse(vparam.tpt)
tparams.foreach(tp => traverse(tp.rhs))
end Extractor
end ExtractSemanticDB
© 2015 - 2025 Weber Informatics LLC | Privacy Policy