使用Scalameta获取引用全限定名及方法引用解析问题排查
Hey there! Let's work through this method reference parsing issue you're hitting with Scalameta and SemanticDB. It's super common to get stuck when moving from tracking explicit method calls to handling method references (whether explicit like foo _ or implicit when passing a method as a higher-order function argument), so let's break this down.
First, Why the Difference?
The key thing here is that method calls and method references are represented differently in both the AST and SemanticDB:
- Explicit method calls (like
someRecognizeableName()) show up asApplynodes in the Scalameta AST, and their semantic references are straightforwardly linked to the method's symbol. - Method references (either explicit
someRecognizeableName _or implicit when used as a function argument) get handled via eta expansion by the compiler. In the AST, this might be aTerm.Etanode (for explicit_syntax) or even just aTerm.Ident/Term.Selectnode (for implicit expansion), and their semantic linking isn't as obvious in the rawdb.nameslist.
Fixes to Track Method References
Here are actionable steps to adjust your code to catch these references:
1. Expand Your AST Node Handling
Don't just target Apply nodes—add cases for nodes that represent method references:
import scala.meta._ import scala.meta.semanticdb._ def traverseCode(term: Term)(implicit ctx: SemanticContext): Unit = term match { // Handle explicit method calls case Apply(_, _) => term.symbol.foreach { sym => println(s"Found method call: ${sym.displayName}") } // Handle explicit method references (e.g., someRecognizeableName _) case Term.Eta(underlyingTerm) => underlyingTerm.symbol.foreach { sym => println(s"Found explicit method reference: ${sym.displayName}") } // Handle implicit method references (passed as higher-order function args) case ref @ (Term.ident(_) | Term.Select(_, _)) if ref.symbol.exists(_.isMethod) => ref.symbol.foreach { sym => println(s"Found implicit method reference: ${sym.displayName}") } // Recursively check all child nodes case other => other.children.foreach { case t: Term => traverseCode(t) case _ => // Skip non-term nodes } }
2. Use SemanticDB References Instead of Just db.names
The db.names list only captures raw identifier text, which doesn't carry the semantic context needed to link method references to their original method symbols. Instead, use the db.references collection, which maps positions directly to their associated symbols.
For example, to find all references to your someRecognizeableName method:
// Get the fully qualified symbol for your method (adjust the signature as needed) val targetMethodSymbol = Symbol("codelab/FindMe#someRecognizeableName().") // Filter all references that point to this symbol val methodReferences = db.references.filter(_.symbol == targetMethodSymbol) methodReferences.foreach { ref => println(s"Found reference at position: ${ref.position.startLine}:${ref.position.startColumn}") }
3. Double-Check Your SemanticDB Setup
Make sure your semanticdb-scalac plugin is configured correctly to capture all necessary semantic info:
- Ensure you've set the
sourcerootcorrectly in your build (matches the root of your project) - Verify that the plugin is enabled for all modules you're analyzing
- Check that you're loading the correct
.semanticdbfiles (they should be intarget/scala-<version>/semanticdbif using sbt)
Quick Troubleshooting Tip
If you're still seeing odd entries, print out the symbol's full display name and kind for each reference:
ref.symbol.foreach { sym => println(s"Symbol: ${sym.displayName}, Kind: ${sym.kind}, Owner: ${sym.owner}") }
This will help you confirm if a reference is actually pointing to your target method, even if it's wrapped in an eta-expanded function.
内容的提问来源于stack exchange,提问作者Gabor Juhasz

