Skip to content

Commit 3d17b02

Browse files
authored
Merge pull request #8388 from tegonal/#5498-postFix-error
Fix #5498: error for postfixOps if language feature not enabled
2 parents ad7da2f + 6a65c8e commit 3d17b02

File tree

15 files changed

+98
-15
lines changed

15 files changed

+98
-15
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,16 @@ object desugar {
16751675
}
16761676
else {
16771677
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
1678+
if (!ctx.featureEnabled(nme.postfixOps)) {
1679+
ctx.error(
1680+
s"""postfix operator `${op.name}` needs to be enabled
1681+
|by making the implicit value scala.language.postfixOps visible.
1682+
|----
1683+
|This can be achieved by adding the import clause 'import scala.language.postfixOps'
1684+
|or by setting the compiler option -language:postfixOps.
1685+
|See the Scaladoc for value scala.language.postfixOps for a discussion
1686+
|why the feature needs to be explicitly enabled.""".stripMargin, t.sourcePos)
1687+
}
16781688
Select(t, op.name)
16791689
}
16801690
case PrefixOp(op, t) =>

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ object StdNames {
540540
val ordinalDollar_ : N = "_$ordinal"
541541
val origin: N = "origin"
542542
val parts: N = "parts"
543+
val postfixOps: N = "postfixOps"
543544
val prefix : N = "prefix"
544545
val processEscapes: N = "processEscapes"
545546
val productArity: N = "productArity"

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2201,7 +2201,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
22012201

22022202
/** Show subtype goal that led to an assertion failure */
22032203
def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context): Unit = {
2204-
println(i"assertion failure for ${show(tp1)} <:< ${show(tp2)}, frozen = $frozenConstraint")
2204+
ctx.echo(i"assertion failure for ${show(tp1)} <:< ${show(tp2)}, frozen = $frozenConstraint")
22052205
def explainPoly(tp: Type) = tp match {
22062206
case tp: TypeParamRef => ctx.echo(s"TypeParamRef ${tp.show} found in ${tp.binder.show}")
22072207
case tp: TypeRef if tp.symbol.exists => ctx.echo(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
500500
if (!sym.exists) ""
501501
else toPrefix(sym.owner) + sym.name + "."
502502
val featureName = toPrefix(owner) + feature
503-
ctx.base.settings.language.value exists (s => s == featureName)
503+
ctx.base.settings.language.value contains featureName
504504
}
505505
hasOption || hasImport
506506
}

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ trait Reporting { this: Context =>
135135
def error(ex: TypeError, pos: SourcePosition): Unit = {
136136
error(ex.toMessage, pos, sticky = true)
137137
if (ctx.settings.YdebugTypeError.value)
138-
ex.printStackTrace
138+
ex.printStackTrace()
139139
}
140140

141141
def errorOrMigrationWarning(msg: Message, pos: SourcePosition = NoSourcePosition): Unit =

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ class CompilationTests extends ParallelTesting {
6161
compileFile("tests/pos-special/i7296.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")),
6262
compileFile("tests/pos-special/notNull.scala", defaultOptions.and("-Yexplicit-nulls")),
6363
compileDir("tests/pos-special/adhoc-extension", defaultOptions.and("-strict", "-feature", "-Xfatal-warnings")),
64-
compileFile("tests/pos-special/i7575.scala", defaultOptions.and("-language:dynamics")),
64+
compileFile("tests/pos-special/i7575.scala", defaultOptions.andLanguageFeature("dynamics")),
6565
compileFile("tests/pos-special/kind-projector.scala", defaultOptions.and("-Ykind-projector")),
6666
compileFile("tests/run/i5606.scala", defaultOptions.and("-Yretain-trees")),
67+
compileFile("tests/pos-custom-args/i5498-postfixOps.scala", defaultOptions withoutLanguageFeature "postfixOps"),
6768
).checkCompile()
6869
}
6970

@@ -128,7 +129,7 @@ class CompilationTests extends ParallelTesting {
128129
compileFile("tests/neg-custom-args/i3246.scala", scala2CompatMode),
129130
compileFile("tests/neg-custom-args/overrideClass.scala", scala2CompatMode),
130131
compileFile("tests/neg-custom-args/ovlazy.scala", scala2CompatMode.and("-migration", "-Xfatal-warnings")),
131-
compileFile("tests/neg-custom-args/autoTuplingTest.scala", defaultOptions.and("-language:noAutoTupling")),
132+
compileFile("tests/neg-custom-args/autoTuplingTest.scala", defaultOptions.andLanguageFeature("noAutoTupling")),
132133
compileFile("tests/neg-custom-args/nopredef.scala", defaultOptions.and("-Yno-predef")),
133134
compileFile("tests/neg-custom-args/noimports.scala", defaultOptions.and("-Yno-imports")),
134135
compileFile("tests/neg-custom-args/noimports2.scala", defaultOptions.and("-Yno-imports")),
@@ -153,9 +154,10 @@ class CompilationTests extends ParallelTesting {
153154
compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings")),
154155
compileFile("tests/neg-custom-args/extmethods-tparams.scala", defaultOptions.and("-deprecation", "-Xfatal-warnings")),
155156
compileDir("tests/neg-custom-args/adhoc-extension", defaultOptions.and("-strict", "-feature", "-Xfatal-warnings")),
156-
compileFile("tests/neg/i7575.scala", defaultOptions.and("-language:_")),
157+
compileFile("tests/neg/i7575.scala", defaultOptions.withoutLanguageFeatures.and("-language:_")),
157158
compileFile("tests/neg-custom-args/kind-projector.scala", defaultOptions.and("-Ykind-projector")),
158159
compileFile("tests/neg-custom-args/typeclass-derivation2.scala", defaultOptions.and("-Yerased-terms")),
160+
compileFile("tests/neg-custom-args/i5498-postfixOps.scala", defaultOptions withoutLanguageFeature "postfixOps"),
159161
).checkExpectedErrors()
160162
}
161163

@@ -223,7 +225,7 @@ class CompilationTests extends ParallelTesting {
223225
Properties.compilerInterface, Properties.scalaLibrary, Properties.scalaAsm,
224226
Properties.dottyInterfaces, Properties.jlineTerminal, Properties.jlineReader,
225227
).mkString(File.pathSeparator),
226-
Array("-Ycheck-reentrant", "-Yemit-tasty-in-class")
228+
Array("-Ycheck-reentrant", "-Yemit-tasty-in-class", "-language:postfixOps")
227229
)
228230

229231
val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped"))

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,11 +626,14 @@ trait ParallelTesting extends RunnerOrchestration { self =>
626626
lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
627627
lazy val actualErrors = reporters.foldLeft(0)(_ + _.errorCount)
628628
def hasMissingAnnotations = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
629+
def showErrors = "-> following the errors:\n" +
630+
reporters.flatMap(_.allErrors.map(e => e.pos.toString + ": " + e.message)).mkString(start = "at ", sep = "\n at ", end = "")
629631

630632
if (compilerCrashed) Some(s"Compiler crashed when compiling: ${testSource.title}")
631633
else if (actualErrors == 0) Some(s"\nNo errors found when compiling neg test $testSource")
632-
else if (expectedErrors != actualErrors) Some(s"\nWrong number of errors encountered when compiling $testSource, expected: $expectedErrors, actual: $actualErrors")
633-
else if (hasMissingAnnotations) Some(s"\nErrors found on incorrect row numbers when compiling $testSource")
634+
else if (expectedErrors == 0) Some(s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error")
635+
else if (expectedErrors != actualErrors) Some(s"\nWrong number of errors encountered when compiling $testSource\nexpected: $expectedErrors, actual: $actualErrors " + showErrors)
636+
else if (hasMissingAnnotations) Some(s"\nErrors found on incorrect row numbers when compiling $testSource\n$showErrors")
634637
else if (!errorMap.isEmpty) Some(s"\nExpected error(s) have {<error position>=<unreported error>}: $errorMap")
635638
else None
636639
}
@@ -654,7 +657,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
654657
Source.fromFile(file, "UTF-8").getLines().zipWithIndex.foreach { case (line, lineNbr) =>
655658
val errors = line.toSeq.sliding("// error".length).count(_.unwrap == "// error")
656659
if (errors > 0)
657-
errorMap.put(s"${file.getPath}:${lineNbr}", errors)
660+
errorMap.put(s"${file.getPath}:$lineNbr", errors)
658661

659662
val noposErrors = line.toSeq.sliding("// nopos-error".length).count(_.unwrap == "// nopos-error")
660663
if (noposErrors > 0) {
@@ -663,7 +666,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
663666
errorMap.put("nopos", noposErrors + existing)
664667
}
665668

666-
val possibleTypos = List("//error" -> "// error" , "//nopos-error" -> "// nopos-error")
669+
val possibleTypos = List("//error" -> "// error", "//nopos-error" -> "// nopos-error")
667670
for ((possibleTypo, expected) <- possibleTypos) {
668671
if (line.contains(possibleTypo))
669672
echo(s"Warning: Possible typo in error tag in file ${file.getCanonicalPath}:$lineNbr: found `$possibleTypo` but expected `$expected`")

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ object TestConfiguration {
5151

5252
val yCheckOptions = Array("-Ycheck:all")
5353

54-
val commonOptions = Array("-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions
54+
val commonOptions = Array("-indent", "-language:postfixOps") ++ checkOptions ++ noCheckOptions ++ yCheckOptions
5555
val defaultOptions = TestFlags(basicClasspath, commonOptions)
5656
val withCompilerOptions =
5757
defaultOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath)
@@ -69,7 +69,7 @@ object TestConfiguration {
6969
)
7070
val picklingWithCompilerOptions =
7171
picklingOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath)
72-
val scala2CompatMode = defaultOptions and "-language:Scala2Compat"
72+
val scala2CompatMode = defaultOptions.andLanguageFeature("Scala2Compat")
7373
val explicitUTF8 = defaultOptions and ("-encoding", "UTF8")
7474
val explicitUTF16 = defaultOptions and ("-encoding", "UTF16")
7575

compiler/test/dotty/tools/vulpix/TestFlags.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,29 @@ final case class TestFlags(
2121

2222
def all: Array[String] = Array("-classpath", defaultClassPath) ++ options
2323

24+
def withoutLanguageFeatures: TestFlags = copy(options = withoutLanguageFeaturesOptions)
25+
26+
private val languageFeatureFlag = "-language:"
27+
private def withoutLanguageFeaturesOptions = options.filterNot(_.startsWith(languageFeatureFlag))
28+
29+
// TODO simplify to add `-language:feature` to `options` once
30+
// https://github.com/lampepfl/dotty-feature-requests/issues/107 is implemented
31+
def andLanguageFeature(feature: String) = {
32+
val (languageFeatures, rest) = options.partition(_.startsWith(languageFeatureFlag))
33+
val existingFeatures = if (languageFeatures.isEmpty) languageFeatures.mkString(",") + "," else ""
34+
copy(options = rest ++ Array(languageFeatureFlag + existingFeatures + feature))
35+
}
36+
37+
def withoutLanguageFeature(feature: String) = {
38+
val (languageFeatures, rest) = options.partition(_.startsWith(languageFeatureFlag))
39+
val filteredFeatures = languageFeatures.filter(_ == feature)
40+
val newOptions =
41+
if (filteredFeatures.isEmpty) rest
42+
else rest ++ Array(languageFeatureFlag + filteredFeatures.mkString(","))
43+
44+
copy(options = newOptions)
45+
}
46+
2447
/** Subset of the flags that should be passed to javac. */
2548
def javacFlags: Array[String] = {
2649
val flags = all

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ object Build {
159159
"-unchecked",
160160
"-Xfatal-warnings",
161161
"-encoding", "UTF8",
162-
"-language:existentials,higherKinds,implicitConversions"
162+
"-language:existentials,higherKinds,implicitConversions,postfixOps"
163163
),
164164

165165
javacOptions in (Compile, compile) ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Error: tests/neg-custom-args/i5498-postfixOps.scala:4:2 -------------------------------------------------------------
2+
4 | 1 second // error: usage of postfix operator
3+
| ^
4+
| postfix operator `second` needs to be enabled
5+
| by making the implicit value scala.language.postfixOps visible.
6+
| ----
7+
| This can be achieved by adding the import clause 'import scala.language.postfixOps'
8+
| or by setting the compiler option -language:postfixOps.
9+
| See the Scaladoc for value scala.language.postfixOps for a discussion
10+
| why the feature needs to be explicitly enabled.
11+
-- Error: tests/neg-custom-args/i5498-postfixOps.scala:6:23 ------------------------------------------------------------
12+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator
13+
| ^^^^^^^^^
14+
| postfix operator `contains` needs to be enabled
15+
| by making the implicit value scala.language.postfixOps visible.
16+
| ----
17+
| This can be achieved by adding the import clause 'import scala.language.postfixOps'
18+
| or by setting the compiler option -language:postfixOps.
19+
| See the Scaladoc for value scala.language.postfixOps for a discussion
20+
| why the feature needs to be explicitly enabled.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.concurrent.duration._
2+
3+
def test() = {
4+
1 second // error: usage of postfix operator
5+
6+
Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import scala.concurrent.duration._
2+
3+
import scala.language.postfixOps
4+
5+
def test() = {
6+
1 second
7+
8+
Seq(1, 2) filter (List(1,2) contains)
9+
}

tests/pos/i5498-postfixOps.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.concurrent.duration._
2+
3+
def test() = {
4+
// only OK since the defaultOptions in the TestConfiguration includes -language:postfixOps
5+
1 second
6+
7+
Seq(1, 2).filter(List(1,2) contains)
8+
}

0 commit comments

Comments
 (0)