The Scala 2.13 compiler issues helpful migration warnings with the -Xsource:3 flag.
Before moving to the Scala 3 compiler, it’s recommended to enable this flag in Scala 2 and address the new warnings.
Usage information is shown with scalac -Xsource:help.
Migration vs cross-building
With Scala 2.13.14 and newer, the -Xsource:3 flag supports two scenarios:
- Xsource:3enables warnings relevant for migrating a codebase to Scala 3. In addition to new warnings, the flag enables certain benign Scala 3 syntaxes such as- import p.*.
- Adding the -Xsource-features:<features>flag is useful to reduce the maintenance burden of projects that cross-build between Scala 2 and 3. Certain language constructs have been backported from Scala 3 in order to improve compatibility. Instead of warning about a behavior change in Scala 3, it adopts the new behavior.
Warnings as errors, and quick fixes
By default, Scala 3 migration warnings emitted by Scala 2.13 are reported as errors, using the default configuration, -Wconf:cat=scala3-migration:e.
This ensures that migration messaging is more visible.
Diagnostics can be emitted as warnings by specifying -Wconf:cat=scala3-migration:w.
Typically, emitting warnings instead of errors will cause more diagnostics to be reported.
The @nowarn annotation can be used in program sources to suppress individual warnings.
Diagnostics are suppressed before they are promoted to errors, so that @nowarn takes precedence over -Wconf and -Werror.
The Scala 2.13 compiler implements quick fixes for many Scala 3 migration warnings.
Quick fixes are displayed in Metals-based IDEs (not yet in IntelliJ), or they can be applied directly to the source code using the -quickfix flag, for example -quickfix:cat=scala3-migration.
See also scala -quickfix:help.
Enabled Scala 3 syntax
The -Xsource:3 flag enables the following Scala 3 syntaxes in Scala 2:
- import p.*
- import p.m as n
- import p.{given, *}
- case C(xs*)as an alias for- case C(xs @ _*)
- A & Btype intersection as an alias for- A with B
Scala 3 migration warnings in detail
Many Scala 3 migration warnings are easy to understand, e.g., for implicit definitions without an explicit type:
scala> object O { implicit val s = "" }
                               ^
       error: Implicit definition must have explicit type (inferred String) [quickfixable]Enabling Scala 3 features with -Xsource-features
Certain Scala 3 language changes have been backported and can be enabled using -Xsource-features; usage and available features are shown with -Xsource-features:help.
When enabling a feature, the corresponding migration warning is no longer issued.
scala> raw"\u0061"
           ^
       warning: Unicode escapes in raw interpolations are deprecated; use literal characters instead
val res0: String = a
scala> :setting -Xsource:3
scala> raw"\u0061"
           ^
       error: Unicode escapes in raw interpolations are ignored in Scala 3 (or with -Xsource-features:unicode-escapes-raw); use literal characters instead
       Scala 3 migration messages are errors under -Xsource:3. Use -Wconf / @nowarn to filter them or add -Xmigration to demote them to warnings.
       Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=scala3-migration, site=res1
scala> :setting -Xsource-features:unicode-escapes-raw
scala> raw"\u0061"
val res1: String = \u0061For every such language feature, a migration warning is issued under plain -Xsource:3.
Enabling the feature silences the warning and adopts the changed behavior.
To avoid silent language changes when upgrading to a new Scala 2.13 version, it is recommended to enable features explicitly or use a group (e.g., -Xsource-features:v2.13.14).
-Xsource:3-cross is a shorthand for -Xsource:3 -Xsource-features:_.
Changes in language semantics
The following table shows backported Scala 3 language semantics available in -Xsource-features / -Xsource:3-cross.
| Feature flag | -Xsource:3behavior | -Xsource-features/-Xsource:3-crossbehavior | 
|---|---|---|
| any2stringadd:(x: Any) + ""is deprecated | deprecation warning | does not compile, implicit any2stringaddis not inferred | 
| unicode-escapes-raw: unicode escapes in triple-quoted strings and raw interpolations ("""\u0061""") | fatal warning, escape is processed | escape is not processed | 
| leading-infix: leading infix operators continue the previous line 1 | fatal warning, second line is a separate expression | operation continues the previous line | 
| string-context-scope: desugaring of string interpolators usingStringContext | fatal warning if the interpolation references a StringContextin scope different fromscala.StringContext | desugaring always uses scala.StringContext | 
| package-prefix-implicits: an implicit for typep.Ais found in the package prefixp | fatal warning | the package prefix pis no longer part of the implicit search scope | 
| implicit-resolution: specificity during implicit search | fatal warning | use Scala-3-style downwards comparisons for implicit search and overloading resolution | 
| case-apply-copy-access: modifiers of synthetic methods | fatal warning | constructor modifiers are used for apply / copy methods of case classes | 
| case-companion-function: companions are Functions | fatal warning at use site | synthetic case companion objects no longer extend FunctionN, but are adapted at use site with warning | 
| infer-override: override type inference | fatal warning | inferred type of member uses type of overridden member | 
| double-definitions: definitions differing in empty parens 2 | fatal warning | double definition error | 
| eta-expand-always:x.feta-expands (x.f _) even without an expected type | compilation error (“missing argument list”) | x.fexpands to a function value | 
Example 1:
  def f =
    1
    + 2Example 2:
class C(x: Int) {
  def x(): Int = x // allowed in Scala 2, double definition error in Scala 3
}Changes affecting binary encoding
As of Scala 2.13.17, there are 3 changes in -Xsource-features that affect binary encoding of classfiles:
- case-apply-copy-access: the constructor modifiers of case classes (- case class C private[p] (x: Int)) are copied to the synthetic- applyand- copymethods.
- case-companion-function: the synthetic companion objects of case classes no longer extend- FunctionN.
- infer-override: overriding methods without an explicit return type inherit the return type from the parent (instead of using the inferred type of the method body).
For projects that are already cross-building between Scala 2 and 3 with existing releases for both, enabling these changes breaks binary compatibility (make sure to use MiMa to detect such changes). For example, if a library defines
trait A { def f: Object }
class B extends A { def f = "hi" }- enabling -Xsource-features:infer-overridebreaks binary compatibility on Scala 2.13: existing releases haveA.f: String, the new version will haveA.f: Object
- adding an explicit result type A.f: Stringbreaks binary compatibility on Scala 3: existing releases haveA.f: Object
It is possible to work around this using version-dependent source files, see scala/scala-xml#675 as an example.
Instead of implementing such workarounds, it might be easier not to enable changes affecting binary encoding (-Xsource-features:v2.13.14,-case-apply-copy-access,-case-companion-function,-infer-override).