Skip to content

Commit

Permalink
Deprecation warnings for old syntax: alphanumeric infix operators
Browse files Browse the repository at this point in the history
This is the first part of #18870
  • Loading branch information
nicolasstucki committed Nov 21, 2023
1 parent 5f4f7c6 commit f2f004d
Show file tree
Hide file tree
Showing 26 changed files with 121 additions and 29 deletions.
16 changes: 12 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1071,12 +1071,19 @@ trait Checking {
def checkValidInfix(tree: untpd.InfixOp, meth: Symbol)(using Context): Unit = {
tree.op match {
case id @ Ident(name: Name) =>
def methCompiledBeforeDeprecation =
meth.compilationUnitInfo != null && {
meth.compilationUnitInfo.nn.tastyVersion match
case Some(version) => version.minor < 4 // compiled before 3.4
case _ => false // compiled with the current compiler
}
name.toTermName match {
case name: SimpleName
if !untpd.isBackquoted(id) &&
!name.isOperatorName &&
!meth.isDeclaredInfix &&
!meth.maybeOwner.is(Scala2x) &&
!methCompiledBeforeDeprecation &&
!infixOKSinceFollowedBy(tree.right) =>
val (kind, alternative) =
if (ctx.mode.is(Mode.Type))
Expand All @@ -1085,13 +1092,14 @@ trait Checking {
("extractor", (n: Name) => s"prefix syntax $n(...)")
else
("method", (n: Name) => s"method syntax .$n(...)")
def rewriteMsg = Message.rewriteNotice("The latter", version = `future-migration`)
report.errorOrMigrationWarning(
def rewriteMsg = Message.rewriteNotice("The latter", version = `3.4-migration`)
report.gradualErrorOrMigrationWarning(
em"""Alphanumeric $kind $name is not declared ${hlAsKeyword("infix")}; it should not be used as infix operator.
|Instead, use ${alternative(name)} or backticked identifier `$name`.$rewriteMsg""",
tree.op.srcPos,
from = future)
if sourceVersion == `future-migration` then {
warnFrom = `3.4`,
errorFrom = future)
if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then {
patch(Span(tree.op.span.start, tree.op.span.start), "`")
patch(Span(tree.op.span.end, tree.op.span.end), "`")
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/test-resources/repl/i1374
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
scala> implicit class Padder(val sb: StringBuilder) extends AnyVal { def pad2(width: Int) = { 1 to width - sb.length foreach { sb append '*' }; sb } }
scala> implicit class Padder(val sb: StringBuilder) extends AnyVal { infix def pad2(width: Int) = { 1 to width - sb.length foreach { sb append '*' }; sb } }
// defined class Padder
def Padder(sb: StringBuilder): Padder
scala> val greeting = new StringBuilder("Hello, kitteh!")
Expand Down
2 changes: 1 addition & 1 deletion compiler/test-resources/type-printer/infix
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def foo: (Int &: String) & Boolean
scala> def foo: Int &: (Boolean & String) = ???
def foo: Int &: (Boolean & String)
scala> import scala.annotation.showAsInfix
scala> @scala.annotation.showAsInfix class Mappy[T,U]
scala> @scala.annotation.showAsInfix infix class Mappy[T,U]
// defined class Mappy
scala> def foo: (Int Mappy Boolean) && String = ???
def foo: (Int Mappy Boolean) && String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import PositionContext.PosCtx
class CodeMarker(val name: String) extends Embedded {

/** A range of positions between this marker and `other`. */
def to(other: CodeMarker): CodeRange = CodeRange(this, other)
infix def to(other: CodeMarker): CodeRange = CodeRange(this, other)

/** The file containing this marker. */
def file: PosCtx[TestFile] = posCtx.positionOf(this)._1
Expand Down
4 changes: 2 additions & 2 deletions scaladoc-testcases/src/tests/infixTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ package infixTypes
import annotation.showAsInfix

@showAsInfix
trait SomeTrait[A, B]
infix trait SomeTrait[A, B]

trait SomeTrait2[A, B]
infix trait SomeTrait2[A, B]

def someTrait1[C, D]: C SomeTrait D
= ???
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,9 @@ object MemberLookup extends MemberLookup {
// Scaladoc overloading support allows terminal * (and they're meaningless)
val cleanStr = str.stripSuffix("*")

if cleanStr endsWith "$" then
if cleanStr.endsWith("$") then
Selector(cleanStr.init, SelectorKind.ForceTerm)
else if cleanStr endsWith "!" then
else if cleanStr.endsWith("!") then
Selector(cleanStr.init, SelectorKind.ForceType)
else
Selector(cleanStr, SelectorKind.NoForce)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ sealed class CharReader(buffer: String) { reader =>

var offset: Int = 0
def char: Char =
if (offset >= buffer.length) endOfText else buffer charAt offset
if (offset >= buffer.length) endOfText else buffer.charAt(offset)

final def nextChar() =
offset += 1
Expand Down Expand Up @@ -712,7 +712,7 @@ sealed class CharReader(buffer: String) { reader =>
jumpWhitespace()
val (ok0, chars0) =
if (chars.charAt(0) == ' ')
(offset > poff, chars substring 1)
(offset > poff, chars.substring(1))
else
(true, chars)
val ok = ok0 && jump(chars0)
Expand Down
2 changes: 1 addition & 1 deletion scaladoc/test/dotty/tools/scaladoc/testUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def tastyFiles(name: String, allowEmpty: Boolean = false, rootPck: String = "tes
}
def collectFiles(dir: File): List[File] = listFilesSafe(dir).toList.flatMap {
case f if f.isDirectory => collectFiles(f)
case f if f.getName endsWith ".tasty" => f :: Nil
case f if f.getName.endsWith(".tasty") => f :: Nil
case _ => Nil
}
val outputDir = BuildInfo.test_testcasesOutputDir
Expand Down
4 changes: 2 additions & 2 deletions tests/init-global/pos/i18628.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ trait Parsers {
}

def flatMap[U](f: T => Parser[U]): Parser[U]
= Parser{ in => this(in) flatMapWithNext(f)}
= Parser{ in => this(in).flatMapWithNext(f)}

def map[U](f: T => U): Parser[U] //= flatMap{x => success(f(x))}
= Parser{ in => this(in) map(f)}
= Parser{ in => this(in).map(f)}

def ^^ [U](f: T => U): Parser[U] = map(f)
}
Expand Down
12 changes: 6 additions & 6 deletions tests/init/pos/Properties.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ private[scala] trait PropertiesTrait {
/** The loaded properties */
protected lazy val scalaProps: java.util.Properties = {
val props = new java.util.Properties
val stream = pickJarBasedOn getResourceAsStream propFilename
val stream = pickJarBasedOn.getResourceAsStream(propFilename)
if (stream ne null)
quietlyDispose(props load stream, stream.close)
quietlyDispose(props.load(stream), stream.close)

props
}
Expand All @@ -47,8 +47,8 @@ private[scala] trait PropertiesTrait {
final def setProp(name: String, value: String) = System.setProperty(name, value)
final def clearProp(name: String) = System.clearProperty(name)

final def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt
final def envOrNone(name: String) = Option(System getenv name)
final def envOrElse(name: String, alt: String) = Option(System.getenv(name)) getOrElse alt
final def envOrNone(name: String) = Option(System.getenv(name))

final def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt

Expand All @@ -68,7 +68,7 @@ private[scala] trait PropertiesTrait {
val releaseVersion =
for {
v <- scalaPropOrNone("maven.version.number")
if !(v endsWith "-SNAPSHOT")
if !(v.endsWith("-SNAPSHOT"))
} yield v

/** The development Scala version, if this is not a final release.
Expand All @@ -82,7 +82,7 @@ private[scala] trait PropertiesTrait {
val developmentVersion =
for {
v <- scalaPropOrNone("maven.version.number")
if v endsWith "-SNAPSHOT"
if v.endsWith("-SNAPSHOT")
ov <- scalaPropOrNone("version.number")
} yield ov

Expand Down
2 changes: 1 addition & 1 deletion tests/init/pos/i15465.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class TestSuite:
protected val it = new ItWord

protected final class ItWord:
def should(string: String) = new ItVerbString("should", string)
infix def should(string: String) = new ItVerbString("should", string)

private def registerTestToRun(fun: => Any): Unit = ()

Expand Down
11 changes: 11 additions & 0 deletions tests/neg/alphanumeric-infix-operator-3.4.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//> using options -Werror

import language.`3.4`

class Foo:
def x(i: Int) = i
infix def y(i: Int) = i

def test(foo: Foo): Unit =
foo x 1 // error (because it was compiled with 3.4+)
foo y 2 // ok: is marked as infix
6 changes: 6 additions & 0 deletions tests/neg/alphanumeric-infix-operator.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Error: tests/neg/alphanumeric-infix-operator.scala:8:6 --------------------------------------------------------------
8 | foo x 1 // error (because it was compiled with 3.4+)
| ^
| Alphanumeric method x is not declared infix; it should not be used as infix operator.
| Instead, use method syntax .x(...) or backticked identifier `x`.
| The latter can be rewritten automatically under -rewrite -source 3.4-migration.
9 changes: 9 additions & 0 deletions tests/neg/alphanumeric-infix-operator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//> using options -Werror

class Foo:
def x(i: Int) = i
infix def y(i: Int) = i

def test(foo: Foo): Unit =
foo x 1 // error (because it was compiled with 3.4+)
foo y 2 // ok: is marked as infix
8 changes: 4 additions & 4 deletions tests/neg/i10901.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ object BugExp4Point2D {

// N - N
@targetName("point2DConstant")
def º(y: T2): Point2D[T1,T2] = ???
infix def º(y: T2): Point2D[T1,T2] = ???


// N - C
@targetName("point2DConstantData")
def º(y: ColumnType[T2]): Point2D[T1,T2] = ???
infix def º(y: ColumnType[T2]): Point2D[T1,T2] = ???



extension [T1:Numeric, T2:Numeric](x: ColumnType[T1])
// C - C
@targetName("point2DData")
def º(y: ColumnType[T2]): Point2D[T1,T2] = ???
infix def º(y: ColumnType[T2]): Point2D[T1,T2] = ???

// C - N
@targetName("point2DDataConstant")
def º(y: T2): Point2D[T1,T2] = ???
infix def º(y: T2): Point2D[T1,T2] = ???


}
Expand Down
6 changes: 6 additions & 0 deletions tests/neg/i2033.check
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@
6 | val out = new ObjectOutputStream(println)
| ^^^^^^^
|method println is eta-expanded even though java.io.OutputStream does not have the @FunctionalInterface annotation.
-- Warning: tests/neg/i2033.scala:7:18 ---------------------------------------------------------------------------------
7 | val arr = bos toByteArray () // error
| ^^^^^^^^^^^
| Alphanumeric method toByteArray is not declared infix; it should not be used as infix operator.
| Instead, use method syntax .toByteArray(...) or backticked identifier `toByteArray`.
| The latter can be rewritten automatically under -rewrite -source 3.4-migration.
2 changes: 1 addition & 1 deletion tests/neg/rewrite-messages.check
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
| ^^^
| Alphanumeric method foo is not declared infix; it should not be used as infix operator.
| Instead, use method syntax .foo(...) or backticked identifier `foo`.
| The latter can be rewritten automatically under -rewrite -source future-migration.
| The latter can be rewritten automatically under -rewrite -source 3.4-migration.
6 changes: 6 additions & 0 deletions tests/neg/syntax-error-recovery.check
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,9 @@
| Discarded non-Unit value of type Null. You may want to use `()`.
|
| longer explanation available when compiling with `-explain`
-- Warning: tests/neg/syntax-error-recovery.scala:61:2 -----------------------------------------------------------------
61 | println(bam)
| ^^^^^^^
| Alphanumeric method println is not declared infix; it should not be used as infix operator.
| Instead, use method syntax .println(...) or backticked identifier `println`.
| The latter can be rewritten automatically under -rewrite -source 3.4-migration.
3 changes: 3 additions & 0 deletions tests/pos/alphanumeric-infix-operator-compat/A_1_c3.0.0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class A:
def x(i: Int) = i
infix def y(i: Int) = i
3 changes: 3 additions & 0 deletions tests/pos/alphanumeric-infix-operator-compat/B_1_c3.1.0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class B:
def x(i: Int) = i
infix def y(i: Int) = i
3 changes: 3 additions & 0 deletions tests/pos/alphanumeric-infix-operator-compat/C_1_c3.2.0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class C:
def x(i: Int) = i
infix def y(i: Int) = i
3 changes: 3 additions & 0 deletions tests/pos/alphanumeric-infix-operator-compat/D_1_c3.3.0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class D:
def x(i: Int) = i
infix def y(i: Int) = i
15 changes: 15 additions & 0 deletions tests/pos/alphanumeric-infix-operator-compat/Test3.4_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//> using options -Werror

import language.`3.4`

def test1(a: A, b: B, c: C, d: D): Unit =
a x 1 // ok: was compiled with 3.0
b x 1 // ok: was compiled with 3.1
c x 1 // ok: was compiled with 3.2
d x 1 // ok: was compiled with 3.3

// ok: is marked as infix
a y 2
b y 2
c y 2
d y 2
13 changes: 13 additions & 0 deletions tests/pos/alphanumeric-infix-operator-compat/TestFuture_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import language.future

def test2(a: A, b: B, c: C, d: D): Unit =
a x 1 // ok: was compiled with 3.0
b x 1 // ok: was compiled with 3.1
c x 1 // ok: was compiled with 3.2
d x 1 // ok: was compiled with 3.3

// ok: is marked as infix
a y 2
b y 2
c y 2
d y 2
2 changes: 1 addition & 1 deletion tests/pos/i7424c.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//> using options -Werror
object Main extends App:
enum Extends[A, B]:
infix enum Extends[A, B]:
case Ev[B, A <: B]() extends (A Extends B)

def cast(a: A): B = this match {
Expand Down
6 changes: 6 additions & 0 deletions tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -3460,6 +3460,7 @@ Text => empty
Language => Scala
Symbols => 12 entries
Occurrences => 33 entries
Diagnostics => 1 entries
Synthetics => 4 entries

Symbols:
Expand Down Expand Up @@ -3511,6 +3512,11 @@ Occurrences:
[20:4..20:13): scalameta -> scala/reflect/Selectable#selectDynamic().
[21:4..21:19): StructuralTypes -> example/StructuralTypes.

Diagnostics:
[14:20..14:23): [warning] Alphanumeric method foo is not declared infix; it should not be used as infix operator.
Instead, use method syntax .foo(...) or backticked identifier `foo`.
The latter can be rewritten automatically under -rewrite -source 3.4-migration.

Synthetics:
[12:2..12:6):user => reflectiveSelectable(*)
[13:2..13:6):user => reflectiveSelectable(*)
Expand Down

0 comments on commit f2f004d

Please sign in to comment.