Scalafix

Scalafix

  • User guide
  • Developer guide
  • Browse sources
  • GitHub

›API Reference

Implementing rules

  • Setup
  • Before you write code
  • Tutorial
  • Local rules

API Reference

  • Overview
  • Patch
  • SymbolMatcher
  • SymbolInformation
  • SemanticType
  • SemanticTree

Contributing

  • Guide
Edit

SymbolInformation

Source: SymbolInformation.scala

SymbolInformation is a data structure containing metadata about a Symbol definition. A symbol information describes the symbols's

  • display name: the identifier used to reference this symbol
  • language: Scala, Java
  • kind: class, trait, object, ...
  • properties: final, abstract, implicit
  • type signature: class declarations, class parents, method parameters, ...
  • visibility access: private, protected, ...
  • overridden symbols: list of symbols that this symbol overrides

Cookbook

All code examples in this document assume you have the following imports in scope

import scalafix.v1._
import scala.meta._

Get symbol of a tree

Use Tree.symbol to get the symbol of a tree. Consider the following code.

println(42)
println()

To get the println symbol we match against the Term.Name("println") tree node.

doc.tree.collect {
  case apply @ Term.Apply.After_4_6_0(println @ Term.Name("println"), _) =>
    (apply.syntax, println.symbol)
}
// res1: List[(String, Symbol)] = List(
//   ("println(42)", scala/Predef.println(+1).),
//   ("println()", scala/Predef.println().)
// )

Lookup method return type

Use MethodSignature.returnType to inspect the return type of a method.

def printReturnType(symbol: Symbol): Unit = {
  symbol.info.get.signature match {
    case signature @ MethodSignature(_, _, returnType) =>
      println("returnType = " + returnType)
      println("signature  = " + signature)
      println("structure  = " + returnType.structure)
  }
}
printReturnType(Symbol("scala/Int#`+`()."))
// returnType = String
// signature  = (x: String): String
// structure  = TypeRef(NoType, Symbol("scala/Predef.String#"), List())
printReturnType(Symbol("scala/Int#`+`(+4)."))
// returnType = Int
// signature  = (x: Int): Int
// structure  = TypeRef(NoType, Symbol("scala/Int#"), List())
printReturnType(Symbol("scala/Option#map()."))
// returnType = Option[B]
// signature  = [B](f: Function1[A,B]): Option[B]
// structure  = TypeRef(
//   NoType,
//   Symbol("scala/Option#"),
//   List(TypeRef(NoType, Symbol("scala/Option#map().[B]"), List()))
// )

The return type for constructor method signatures is always NoType.

printReturnType(Symbol("scala/Some#`<init>`()."))
// returnType = <no type>
// signature  = (value: A): <no type>
// structure  = NoType

Lookup method parameters

Consider the following program.

// Main.scala
package example
class Main(val constructorParam: Int) {
  def magic: Int = 42
  def typeParam[T]: T = ???
  def annotatedParam(@deprecatedName('a) e: Int): Int = e
  def curried(a: Int)(b: Int) = a + b
}

Use MethodSignature.parameterLists to look up parameters of a method.

def printMethodParameters(symbol: Symbol): Unit = {
  symbol.info.get.signature match {
    case signature @ MethodSignature(typeParameters, parameterLists, _) =>
      println("signature  = " + signature)
      if (typeParameters.nonEmpty) {
        println("typeParameters")
        println(typeParameters.mkString("  ", "\n  ", ""))
      }
      parameterLists.foreach { parameterList =>
        println("parametersList")
        println(parameterList.mkString("  ", "\n  ", ""))
      }
  }
}
printMethodParameters(Symbol("example/Main#magic()."))
// signature  = : Int
printMethodParameters(Symbol("example/Main#typeParam()."))
// signature  = [T]: T
// typeParameters
//   example/Main#typeParam().[T] => typeparam T
printMethodParameters(Symbol("example/Main#annotatedParam()."))
// signature  = (e: Int): Int
// parametersList
//   example/Main#annotatedParam().(e) => @deprecatedName param e: Int
printMethodParameters(Symbol("example/Main#`<init>`()."))
// signature  = (constructorParam: Int): <no type>
// parametersList
//   example/Main#`<init>`().(constructorParam) => val param constructorParam: Int

Curried methods are distinguished by a MethodSignature with a parameter list of length greater than 1.

printMethodParameters(Symbol("example/Main#curried()."))
// signature  = (a: Int)(b: Int): Int
// parametersList
//   example/Main#curried().(a) => param a: Int
// parametersList
//   example/Main#curried().(b) => param b: Int
printMethodParameters(Symbol("scala/Option#fold()."))
// signature  = [B](ifEmpty: => B)(f: Function1[A,B]): B
// typeParameters
//   scala/Option#fold().[B] => typeparam B
// parametersList
//   scala/Option#fold().(ifEmpty) => param ifEmpty: => B
// parametersList
//   scala/Option#fold().(f) => param f: Function1[A, B]

Test if method is nullary

A "nullary method" is a method that is declared with no parameters and without parentheses.

// Main.scala
package example
object Main {
  def nullary: Int = 1
  def nonNullary(): Unit = println(2)
  def toString = "Main"
}

Nullary method signatures are distinguished by having an no parameter lists: List().

def printParameterList(symbol: Symbol): Unit = {
  symbol.info.get.signature match {
    case MethodSignature(_, parameterLists, _) =>
      println(parameterLists)
  }
}
printParameterList(Symbol("example/Main.nullary()."))
// List()
printParameterList(Symbol("scala/collection/Iterator#hasNext()."))
// List()

Non-nullary methods such as Iterator.next() have a non-empty list of parameters: List(List()).

printParameterList(Symbol("example/Main.nonNullary()."))
// List(List())
printParameterList(Symbol("scala/collection/Iterator#next()."))
// List(List())

Java does not have nullary methods so Java methods always have a non-empty list: List(List()).

printParameterList(Symbol("java/lang/String#isEmpty()."))
// List(List())
printParameterList(Symbol("java/lang/String#toString()."))
// List(List())

Scala methods that override Java methods always have non-nullary signatures even if the Scala method is defined as nullary without parentheses.

printParameterList(Symbol("example/Main.toString()."))
// List(List())

Lookup type alias

Use TypeSignature to inspect type aliases.

def printTypeAlias(symbol: Symbol): Unit = {
  symbol.info.get.signature match {
    case signature @ TypeSignature(typeParameters, lowerBound, upperBound) =>
      if (lowerBound == upperBound) {
        println("Type alias where upperBound == lowerBound")
        println("signature      = '" + signature + "'")
        println("typeParameters = " + typeParameters.structure)
        println("bound          = " + upperBound.structure)
      } else {
        println("Different upper and lower bounds")
        println("signature = '" + signature + "'")
        println("structure = " + signature.structure)
      }
  }
}

Consider the following program.

// Main.scala
package example
object Main {
  type Number = Int
  type Sequence[T] = Seq[T]
  type Unbound
  type LowerBound >: Int
  type UpperBound <: String
  type UpperAndLowerBounded  >: String <: CharSequence
}
printTypeAlias(Symbol("example/Main.Number#"))
// Type alias where upperBound == lowerBound
// signature      = ' = Int'
// typeParameters = List()
// bound          = TypeRef(NoType, Symbol("scala/Int#"), List())
printTypeAlias(Symbol("example/Main.Sequence#"))
// Type alias where upperBound == lowerBound
// signature      = '[T] = Seq[T]'
// typeParameters = List(SymbolInformation(example/Main.Sequence#[T] => typeparam T))
// bound          = TypeRef(
//   NoType,
//   Symbol("scala/package.Seq#"),
//   List(TypeRef(NoType, Symbol("example/Main.Sequence#[T]"), List()))
// )
printTypeAlias(Symbol("example/Main.Unbound#"))
// Different upper and lower bounds
// signature = ''
// structure = TypeSignature(
//   List(),
//   TypeRef(NoType, Symbol("scala/Nothing#"), List()),
//   TypeRef(NoType, Symbol("scala/Any#"), List())
// )
printTypeAlias(Symbol("example/Main.LowerBound#"))
// Different upper and lower bounds
// signature = ' >: Int'
// structure = TypeSignature(
//   List(),
//   TypeRef(NoType, Symbol("scala/Int#"), List()),
//   TypeRef(NoType, Symbol("scala/Any#"), List())
// )
printTypeAlias(Symbol("example/Main.UpperBound#"))
// Different upper and lower bounds
// signature = ' <: String'
// structure = TypeSignature(
//   List(),
//   TypeRef(NoType, Symbol("scala/Nothing#"), List()),
//   TypeRef(NoType, Symbol("scala/Predef.String#"), List())
// )
printTypeAlias(Symbol("example/Main.UpperAndLowerBounded#"))
// Different upper and lower bounds
// signature = ' >: String <: CharSequence'
// structure = TypeSignature(
//   List(),
//   TypeRef(NoType, Symbol("scala/Predef.String#"), List()),
//   TypeRef(NoType, Symbol("java/lang/CharSequence#"), List())
// )

Lookup class parents

Use ClassSignature.parents and TypeRef.symbol to lookup the class hierarchy.

def getParentSymbols(symbol: Symbol): Set[Symbol] =
  symbol.info.get.signature match {
    case ClassSignature(_, parents, _, _) =>
      Set(symbol) ++ parents.collect {
        case TypeRef(_, symbol, _) => getParentSymbols(symbol)
      }.flatten
  }
getParentSymbols(Symbol("java/lang/String#"))
// res28: Set[Symbol] = HashSet(
//   java/lang/String#,
//   java/lang/constant/ConstantDesc#,
//   java/io/Serializable#,
//   java/lang/CharSequence#,
//   java/lang/Comparable#,
//   java/lang/Object#,
//   java/lang/constant/Constable#
// )

Lookup class methods

Use ClassSignature.declarations and SymbolInformation.isMethod to query methods of a class. Use ClassSignature.parents to query methods that are inherited from supertypes.

def getClassMethods(symbol: Symbol): Set[SymbolInformation] =
  symbol.info.get.signature match {
    case ClassSignature(_, parents, _, declarations) =>
      val methods = declarations.filter(_.isMethod)
      methods.toSet ++ parents.collect {
        case TypeRef(_, symbol, _) => getClassMethods(symbol)
      }.flatten
    case _ => Set.empty
  }
getClassMethods(Symbol("scala/Some#")).take(5)
// res29: Set[SymbolInformation] = HashSet(
//   scala/Product#productPrefix(). => method productPrefix: String,
//   scala/Option#getOrElse(). => @inline final method getOrElse[B >: A](default: => B): B,
//   scala/Option#orNull(). => @inline final method orNull[A1 >: A](implicit ev: <:<[Null, A1]): A1,
//   scala/Any#`!=`(). => final method !=(that: Any): Boolean,
//   scala/Any#isInstanceOf(). => final method isInstanceOf[A](): Boolean
// )
getClassMethods(Symbol("java/lang/String#")).take(5)
// res30: Set[SymbolInformation] = HashSet(
//   java/lang/Object#wait(+1). => final method wait(param0: Long): Unit,
//   java/lang/String#format(). => static method format(param0: String, param1: Object*): String,
//   java/lang/String#toString(). => method toString(): String,
//   java/lang/String#coder(). => private[lang] method coder(): Byte,
//   java/lang/String#encodeWithEncoder(). => private static method encodeWithEncoder(param0: Charset, param1: Byte, param2: Array[Byte], param3: Boolean): Array[Byte]
// )
getClassMethods(Symbol("scala/collection/immutable/List#")).take(5)
// res31: Set[SymbolInformation] = HashSet(
//   scala/collection/IterableOnce#iterator(). => abstract method iterator: Iterator[A],
//   scala/collection/IterableOps#iterateUntilEmpty(). => private[this] method iterateUntilEmpty(f: Function1[Iterable[A], Iterable[A]]): Iterator[C],
//   scala/collection/IterableOnceOps#mkString(). => final method mkString(start: String, sep: String, end: String): String,
//   scala/collection/IterableOnceOps#isTraversableAgain(). => method isTraversableAgain: Boolean,
//   scala/Any#hashCode(). => abstract method hashCode(): Int
// )

For Java methods, use SymbolInformation.isStatic to separate static methods from non-static methods.

getClassMethods(Symbol("java/lang/String#")).filter(_.isStatic).take(3)
// res32: Set[SymbolInformation] = HashSet(
//   java/lang/String#rangeCheck(). => private static method rangeCheck(param0: Array[Char], param1: Int, param2: Int): Void,
//   java/lang/String#valueOf(+7). => static method valueOf(param0: Float): String,
//   java/lang/String#valueOf(+2). => static method valueOf(param0: Array[Char], param1: Int, param2: Int): String
// )
getClassMethods(Symbol("java/lang/String#")).filter(!_.isStatic).take(3)
// res33: Set[SymbolInformation] = HashSet(
//   java/lang/Object#wait(+1). => final method wait(param0: Long): Unit,
//   java/lang/String#translateEscapes(). => method translateEscapes(): String,
//   java/lang/String#toUpperCase(+1). => method toUpperCase(): String
// )

Lookup class primary constructor

A primary constructor is the constructor that defined alongside the class declaration.

// User.scala
package example
class User(name: String, age: Int) {      // primary constructor
  def this(name: String) = this(name, 42) // secondary constructor
}

Use SymbolInformation.{isConstructor,isPrimary} to distinguish a primary constructor.

def getConstructors(symbol: Symbol): List[SymbolInformation] =
  symbol.info.get.signature match {
    case ClassSignature(_, _, _, declarations) =>
      declarations.filter { declaration =>
        declaration.isConstructor
      }
    case _ => Nil
  }
getConstructors(Symbol("example/User#")).filter(_.isPrimary)
// res35: List[SymbolInformation] = List(
//   example/User#`<init>`(). => primary ctor <init>(name: String, age: Int)
// )

// secondary constructors are distinguished by not being primary
getConstructors(Symbol("example/User#")).filter(!_.isPrimary)
// res36: List[SymbolInformation] = List(
//   example/User#`<init>`(+1). => ctor <init>(name: String)
// )

Java constructors cannot be primary, "primary constructor" is a Scala-specific feature.

getConstructors(Symbol("java/lang/String#")).take(3)
// res37: List[SymbolInformation] = List(
//   java/lang/String#`<init>`(). => ctor <init>(),
//   java/lang/String#`<init>`(+1). => ctor <init>(param0: String),
//   java/lang/String#`<init>`(+2). => ctor <init>(param0: Array[Char])
// )
getConstructors(Symbol("java/lang/String#")).filter(_.isPrimary)
// res38: List[SymbolInformation] = List()
getConstructors(Symbol("java/util/ArrayList#")).filter(_.isPrimary)
// res39: List[SymbolInformation] = List()

Lookup case class fields

Consider the following program.

// User.scala
package example
case class User(name: String, age: Int) {
  def this(secondaryName: String) = this(secondaryName, 42)
  val upperCaseName = name.toUpperCase
}

On the symbol information level, there is no difference between name and upperCaseName, both are val method.

println(Symbol("example/User#name.").info)
// Some(value = example/User#name. => val method name: String)
println(Symbol("example/User#upperCaseName.").info)
// Some(
//   value = example/User#upperCaseName. => val method upperCaseName: String
// )

See scalameta/scalameta#1492 for a discussion about adding isSynthetic to distinguish between name and upperCaseName.

Use the primary constructor to get the names of the case class fields

getConstructors(Symbol("example/User#")).foreach {
  case ctor if ctor.isPrimary =>
    ctor.signature match {
      case MethodSignature(_, parameters :: _, _) =>
        val names = parameters.map(_.displayName)
        println("names: " + names.mkString(", "))
    }
  case _ => // secondary constructor, ignore `this(secondaryName: String)`
}
// names: name, age

Lookup method overloads

Use SymbolInformation.{isMethod,displayName} to query for overloaded methods.

def getMethodOverloads(classSymbol: Symbol, methodName: String): Set[SymbolInformation] =
  classSymbol.info.get.signature match {
    case ClassSignature(_, parents, _, declarations) =>
      val overloadedMethods = declarations.filter { declaration =>
        declaration.isMethod &&
        declaration.displayName == methodName
      }
      overloadedMethods.toSet ++ parents.collect {
        case TypeRef(_, symbol, _) => getMethodOverloads(symbol, methodName)
      }.flatten
    case _ => Set.empty
  }
getMethodOverloads(Symbol("java/lang/String#"), "substring")
// res44: Set[SymbolInformation] = Set(
//   java/lang/String#substring(). => method substring(param0: Int): String,
//   java/lang/String#substring(+1). => method substring(param0: Int, param1: Int): String
// )
getMethodOverloads(Symbol("scala/Predef."), "assert")
// res45: Set[SymbolInformation] = Set(
//   scala/Predef.assert(). => @elidable method assert(assertion: Boolean): Unit,
//   scala/Predef.assert(+1). => @elidable @inline final method assert(assertion: Boolean, message: => Any): Unit
// )
getMethodOverloads(Symbol("scala/Predef."), "println")
// res46: Set[SymbolInformation] = Set(
//   scala/Predef.println(). => method println(): Unit,
//   scala/Predef.println(+1). => method println(x: Any): Unit
// )
getMethodOverloads(Symbol("java/io/PrintStream#"), "print").take(3)
// res47: Set[SymbolInformation] = HashSet(
//   java/io/PrintStream#print(). => method print(param0: Boolean): Unit,
//   java/io/PrintStream#print(+5). => method print(param0: Double): Unit,
//   java/io/PrintStream#print(+1). => method print(param0: Char): Unit
// )

Overloaded methods can be inherited from supertypes.

// Main.scala
package example
class Main {
  def toString(width: Int): String = ???
}
getMethodOverloads(Symbol("example/Main#"), "toString")
// res49: Set[SymbolInformation] = Set(
//   example/Main#toString(). => method toString(width: Int): String,
//   scala/Any#toString(). => abstract method toString(): String
// )

Test if symbol is from Java or Scala

Use SymbolInformation.{isScala,isJava} to test if a symbol is defined in Java or Scala.

def printLanguage(symbol: Symbol): Unit =
  if (symbol.info.get.isJava) println("java")
  else if (symbol.info.get.isScala) println("scala")
  else println("unknown")

printLanguage(Symbol("java/lang/String#"))
// java
printLanguage(Symbol("scala/Predef.String#"))
// scala

Package symbols are neither defined in Scala or Java.

printLanguage(Symbol("scala/"))
// unknown
printLanguage(Symbol("java/"))
// unknown

Test if symbol is private

Access modifiers such as private and protected control the visibility of a symbol.

def printAccess(symbol: Symbol): Unit = {
  val info = symbol.info.get
  println(
         if (info.isPrivate) "private"
    else if (info.isPrivateThis) "private[this]"
    else if (info.isPrivateWithin) s"private[${info.within.get.displayName}]"
    else if (info.isProtected) "protected"
    else if (info.isProtectedThis) "protected[this]"
    else if (info.isProtectedWithin) s"protected[${info.within.get.displayName}]"
    else if (info.isPublic) "public"
    else "<no access>"
  )
}

Consider the following program.

// Main.scala
package example
class Main {
                     def publicMethod          = 1
  private            def privateMethod         = 1
  private[this]      def privateThisMethod     = 1
  private[example]   def privateWithinMethod   = 1
  protected          def protectedMethod       = 1
  protected[this]    def protectedThisMethod   = 1
  protected[example] def protectedWithinMethod = 1
}

The methods have the following access modifiers.

printAccess(Symbol("example/Main#publicMethod()."))
// public
printAccess(Symbol("example/Main#privateMethod()."))
// private
printAccess(Symbol("example/Main#privateThisMethod()."))
// private[this]
printAccess(Symbol("example/Main#privateWithinMethod()."))
// private[example]
printAccess(Symbol("example/Main#protectedMethod()."))
// protected
printAccess(Symbol("example/Main#protectedThisMethod()."))
// protected[this]
printAccess(Symbol("example/Main#protectedWithinMethod()."))
// protected[example]

Observe that a symbol can only have one kind of access modifier, for example isPrivate=false for symbols where isPrivateWithin=true.

Java does supports smaller set of access modifiers, there is no private[this], protected[this] and protected[within] for Java symbols.

printAccess(Symbol("java/lang/String#"))
// public

println(Symbol("java/lang/String#value.").info)
// Some(
//   value = java/lang/String#value. => private final field value: Array[Byte]
// )
printAccess(Symbol("java/lang/String#value."))
// private

println(Symbol("java/lang/String#`<init>`(+15).").info)
// Some(
//   value = java/lang/String#`<init>`(+15). => private[lang] ctor <init>(param0: Array[Char], param1: Int, param2: Int, param3: Void)
// )
printAccess(Symbol("java/lang/String#`<init>`(+15)."))
// private[lang]

Package symbols have no access restrictions.

printAccess(Symbol("scala/"))
// <no access>
printAccess(Symbol("java/"))
// <no access>

Lookup symbol annotations

Definitions such as classes, parameters and methods can be annotated with @annotation.

// Main.scala
package example
object Main {
  @deprecated("Use add instead", "1.0")
  def +(a: Int, b: Int) = add(a, b)

  class typed[T] extends scala.annotation.StaticAnnotation
  @typed[Int]
  def add(a: Int, b: Int) = a + b
}

Use SymbolInformation.annotations to query the annotations of a symbol.

def printAnnotations(symbol: Symbol): Unit =
  println(symbol.info.get.annotations.structure)

printAnnotations(Symbol("example/Main.`+`()."))
// List(Annotation(TypeRef(NoType, Symbol("scala/deprecated#"), List())))
printAnnotations(Symbol("example/Main.add()."))
// List(
//   Annotation(TypeRef(
//     NoType,
//     Symbol("example/Main.typed#"),
//     List(TypeRef(NoType, Symbol("scala/Int#"), List()))
//   ))
// )
printAnnotations(Symbol("scala/Predef.identity()."))
// List(Annotation(TypeRef(NoType, Symbol("scala/inline#"), List())))
printAnnotations(Symbol("scala/Function2#[T1]"))
// List(Annotation(TypeRef(NoType, Symbol("scala/specialized#"), List())))

It is not possible to query the term arguments of annotations. For example, observe that the annotation for Main.+ does not include the "Use add instead" message.

Known limitations

Lookup method overrides

Consider the following program.

trait A {
  def add(a: Int, b: Int): Int
}
class B extends A {
  override def add(a: Int, b: Int): Int = a + b
}

There is no API to go from the symbol B#add(). to the symbol it overrides A#add(). There is also no .isOverride helper to test if a method overrides another symbol.

SemanticDB

The structure of SymbolInformation in Scalafix mirrors SemanticDB SymbolInformation. For comprehensive documentation about SemanticDB symbol information consult the SemanticDB specification:

  • General symbol information
  • Scala symbol information
  • Java symbol information

Language

SemanticDB supports two languages: Scala and Java. Every symbol is either defined in Scala or Java. To determine if a symbol is defined in Scala or in Java, use the isScala and isJava methods. A symbol cannot be defined in both Java and Scala.

Kind

Every symbol has exactly one kind such as being a class or an interface. A symbol can't have two kinds, for example it's not possible to be both a constructor and a method. The available symbol kinds are:

  • isLocal
  • isField
  • isMethod
  • isConstructor
  • isMacro
  • isType
  • isParameter
  • isSelfParameter
  • isTypeParameter
  • isObject
  • isPackage
  • isPackageObject
  • isClass
  • isInterface
  • isTrait

Some kinds are limited to specific languages. For example, Scala symbols cannot be fields and Java symbols cannot be traits.

Properties

A symbol can have zero or more properties such as implicit or final. The available symbol properties are:

  • isAbstract
  • isFinal
  • isSealed
  • isImplicit
  • isLazy
  • isCase
  • isCovariant
  • isContravariant
  • isVal
  • isVar
  • isStatic
  • isPrimary
  • isEnum
  • isDefault
  • isGiven
  • isInline
  • isOpen
  • isTransparent
  • isInfix
  • isOpaque

Consult the SemanticDB specification to learn which properties are valid for each kind. For example, Scala traits can only be sealed, it is not valid for a trait to be implicit or final.

Signature

Signature is a sealed data structure that describes the shape of a symbol definition.

sealed abstract class Signature extends Product with Serializable {
  final override def toString: String = Pretty.pretty(this).render(80)
  final def toString(width: Int): String = Pretty.pretty(this).render(width)
  final def isEmpty: Boolean = this == NoSignature
  final def nonEmpty: Boolean = !isEmpty
}

final case class ValueSignature(tpe: SemanticType) extends Signature
final case class ClassSignature(
    typeParameters: List[SymbolInformation],
    parents: List[SemanticType],
    self: SemanticType,
    declarations: List[SymbolInformation]
) extends Signature
final case class MethodSignature(
    typeParameters: List[SymbolInformation],
    parameterLists: List[List[SymbolInformation]],
    returnType: SemanticType
) extends Signature
final case class TypeSignature(
    typeParameters: List[SymbolInformation],
    lowerBound: SemanticType,
    upperBound: SemanticType
) extends Signature
case object NoSignature extends Signature

To learn more about SemanticDB signatures, consult the specification:

  • General signatures
  • Scala signatures
  • Java signatures

Annotation

To learn more about SemanticDB annotations, consult the specification:

  • General annotations
  • Scala annotations
  • Java annotations

Access

Some symbols are only accessible within restricted scopes, such as the enclosing class or enclosing package. A symbol can only have one access, for example is not valid for a symbol to be both private and private within. The available access methods are:

  • isPrivate
  • isPrivateThis
  • isPrivateWithin
  • isProtected
  • isProtectedThis
  • isProtectedWithin
  • isPublic

To learn more about SemanticDB visibility access, consult the specification:

  • General access
  • Scala access
  • Java access

Utility methods

Some attributes of symbols are derived from a combination of language, kind and property values. The following methods are available on SymbolInformation to address common use-cases:

  • isDef: returns true if this symbol is a Scala def.
  • isSetter: returns true if this is a setter symbol. For example, every global var symbol has a corresponding setter symbol. Setter symbols are distinguished by having a display name that ends with _=.
← SymbolMatcherSemanticType →
  • Cookbook
    • Get symbol of a tree
    • Lookup method return type
    • Lookup method parameters
    • Test if method is nullary
    • Lookup type alias
    • Lookup class parents
    • Lookup class methods
    • Lookup class primary constructor
    • Lookup case class fields
    • Lookup method overloads
    • Test if symbol is from Java or Scala
    • Test if symbol is private
    • Lookup symbol annotations
  • Known limitations
    • Lookup method overrides
  • SemanticDB
    • Language
    • Kind
    • Properties
    • Signature
    • Annotation
    • Access
    • Utility methods
Scalafix
Docs
Get startedRulesExtend Scalafix
Community
Chat on DiscordDiscuss on Scala Users
More
GitHub
Copyright © 2025 Scala Center