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

SemanticType

Source: SemanticType.scala

SemanticType is a sealed data structure that encodes the Scala type system.

sealed abstract class SemanticType 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 == NoType
  final def nonEmpty: Boolean = !isEmpty
}
final case class TypeRef(prefix: SemanticType, symbol: Symbol, typeArguments: List[SemanticType]) extends SemanticType
final case class SingleType(prefix: SemanticType, symbol: Symbol) extends SemanticType
final case class ThisType(symbol: Symbol) extends SemanticType
final case class SuperType(prefix: SemanticType, symbol: Symbol) extends SemanticType
final case class ConstantType(constant: Constant) extends SemanticType
final case class IntersectionType(types: List[SemanticType]) extends SemanticType
final case class UnionType(types: List[SemanticType]) extends SemanticType
final case class WithType(types: List[SemanticType]) extends SemanticType
final case class StructuralType(tpe: SemanticType, declarations: List[SymbolInformation]) extends SemanticType
final case class AnnotatedType(annotations: List[Annotation], tpe: SemanticType) extends SemanticType
final case class ExistentialType(tpe: SemanticType, declarations: List[SymbolInformation]) extends SemanticType
final case class UniversalType(typeParameters: List[SymbolInformation], tpe: SemanticType) extends SemanticType
final case class ByNameType(tpe: SemanticType) extends SemanticType
final case class RepeatedType(tpe: SemanticType) extends SemanticType
final case class LambdaType(parameters: List[SymbolInformation], returnType: SemanticType) extends SemanticType
final case class MatchType(scrutinee: SemanticType, cases: List[CaseType]) extends SemanticType
case object NoType extends SemanticType

Cookbook

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

import scalafix.v1._

Lookup symbol of type

Consider the following program.

// Main.scala
package example
object Main {
  val a = 42
  val b = List(42)
}

Use MethodSignature.returnType to get the types of vals.

def getType(symbol: Symbol): SemanticType =
  symbol.info.get.signature match {
    case MethodSignature(_, _, returnType) =>
      returnType
  }
println(getType(Symbol("example/Main.a.")))
// Int
println(getType(Symbol("example/Main.a.")).structure)
// TypeRef(NoType, Symbol("scala/Int#"), List())

println(getType(Symbol("example/Main.b.")))
// List[Int]
println(getType(Symbol("example/Main.b.")).structure)
// TypeRef(
//   NoType,
//   Symbol("scala/collection/immutable/List#"),
//   List(TypeRef(NoType, Symbol("scala/Int#"), List()))
// )

Use TypeRef.symbol to obtain the symbols scala/Int# and scala/collection/immutable/List# from the types Int or List[Int].

def printTypeSymbol(tpe: SemanticType): Unit =
  tpe match {
    case TypeRef(_, symbol, _) =>
      println(symbol)
    case error =>
      println(s"MatchError: $error (of ${error.productPrefix})")
  }
printTypeSymbol(getType(Symbol("example/Main.a.")))
// scala/Int#
printTypeSymbol(getType(Symbol("example/Main.b.")))
// scala/collection/immutable/List#

Beware however that not all types are TypeRef.

// Main.scala
package example
class Main
object Main {
  val a: Main with Serializable = ???
  val b: Main.type = this
}

The printTypeSymbol method crashes on non-TypeRef types.

printTypeSymbol(getType(Symbol("example/Main.a.")))
// MatchError: Main with Serializable {} (of StructuralType)
printTypeSymbol(getType(Symbol("example/Main.b.")))
// MatchError: Main.type (of SingleType)

Known limitations

The SemanticType API is new and intentionally comes with a small public API. Some missing functionality is possible to implement outside of Scalafix.

Lookup type of a term

Consider the following program.

object Main {
  val list = List(1).map(_ + 1)
}

There is no API to inspect the type of tree nodes such as List(1). It is only possible to lookup the signature of the symbol Main.list.

Test for subtyping

Consider the following program.

sealed abstract class A[+T]
case class B(n: Int) extends A[Int]

There is no API to query whether the type B (structure: TypeRef(NoType, "B#", Nil)) is a subtype of A[Int] (structure TypeRef(NoType, "A#", List(TypeRef(NoType, "scala/Int#", Nil)))).

Dealias types

Consider the following program.

// Main.scala
package example
object Main {
  val a = "message"
  val b: String = "message"
  val c: List[String] = List()
}

The a and b variables have semantically the same type but their types are structurally different because the explicit : String annotation for b references a type alias.

println(getType(Symbol("example/Main.a.")).structure)
// TypeRef(NoType, Symbol("java/lang/String#"), List())
println(getType(Symbol("example/Main.b.")).structure)
// TypeRef(NoType, Symbol("scala/Predef.String#"), List())

There is no public API to dealias type. It's possible to prototype roughly similar functionality with the simpleDealias method below, but beware that it is an incomplete implementation.

def simpleDealias(tpe: SemanticType): SemanticType = {
  def dealiasSymbol(symbol: Symbol): Symbol =
    symbol.info.get.signature match {
      case TypeSignature(_, lowerBound @ TypeRef(_, dealiased, _), upperBound)
          if lowerBound == upperBound =>
        dealiased
      case _ =>
        symbol
    }
  tpe match {
    case TypeRef(prefix, symbol, typeArguments) =>
      TypeRef(prefix, dealiasSymbol(symbol), typeArguments.map(simpleDealias))
  }
}
println(simpleDealias(getType(Symbol("example/Main.a."))).structure)
// TypeRef(NoType, Symbol("java/lang/String#"), List())
println(simpleDealias(getType(Symbol("example/Main.b."))).structure)
// TypeRef(NoType, Symbol("java/lang/String#"), List())

The simpleDealias method also handles TypeRef.typeArguments so that scala.List[scala.Predef.String] becomes scala.collection.immutable.List[java.lang.String].

println(getType(Symbol("example/Main.c.")).structure)
// TypeRef(
//   NoType,
//   Symbol("scala/package.List#"),
//   List(TypeRef(NoType, Symbol("scala/Predef.String#"), List()))
// )
println(simpleDealias(getType(Symbol("example/Main.c."))).structure)
// TypeRef(
//   NoType,
//   Symbol("scala/collection/immutable/List#"),
//   List(TypeRef(NoType, Symbol("java/lang/String#"), List()))
// )

SemanticDB

The structure of SemanticType mirrors SemanticDB Type. For comprehensive documentation about SemanticDB types, consult the SemanticDB specification:

  • General types
  • Scala types
  • Java types

The SemanticType data structure diverges from SemanticDB Type in few minor details.

SemanticType instead of Type

Scalafix uses the name SemanticType instead of Type in order to avoid ambiguous references with scala.meta.Type when importing the two packages together.

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

List[SymbolInformation] instead of Scope

The SemanticType data structure uses List[SymbolInformation] instead of Scope, where applicable. This change avoids the notion of "soft-linked" and "hard-linked" symbols, resulting in a higher-level API without loss of expressiveness.

← SymbolInformationSemanticTree →
  • Cookbook
    • Lookup symbol of type
  • Known limitations
    • Lookup type of a term
    • Test for subtyping
    • Dealias types
  • SemanticDB
    • SemanticType instead of Type
    • List[SymbolInformation] instead of Scope
Scalafix
Docs
Get startedRulesExtend Scalafix
Community
Chat on DiscordDiscuss on Scala Users
More
GitHub
Copyright © 2025 Scala Center