Skip to content

Commit b505b08

Browse files
authored
Merge pull request #14677 from dotty-staging/fix-14564
Reject postfix ops already in Parser
2 parents aecdc79 + 8c92ee1 commit b505b08

26 files changed

+86
-74
lines changed

compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ trait BytecodeWriters {
100100
super.writeClass(label, jclassName, jclassBytes, outfile)
101101

102102
val segments = jclassName.split("[./]")
103-
val asmpFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "asmp" toFile;
103+
val asmpFile = segments.foldLeft(baseDir: Path)(_ / _).changeExtension("asmp").toFile
104104

105105
asmpFile.parent.createDirectory()
106106
emitAsmp(jclassBytes, asmpFile)
@@ -131,7 +131,7 @@ trait BytecodeWriters {
131131
super.writeClass(label, jclassName, jclassBytes, outfile)
132132

133133
val pathName = jclassName
134-
val dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile;
134+
val dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _).changeExtension("class").toFile
135135
dumpFile.parent.createDirectory()
136136
val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path))
137137

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,28 +1736,16 @@ object desugar {
17361736
// This is a deliberate departure from scalac, where StringContext is not rooted (See #4732)
17371737
Apply(Select(Apply(scalaDot(nme.StringContext), strs), id).withSpan(tree.span), elems)
17381738
case PostfixOp(t, op) =>
1739-
if ((ctx.mode is Mode.Type) && !isBackquoted(op) && op.name == tpnme.raw.STAR) {
1739+
if (ctx.mode is Mode.Type) && !isBackquoted(op) && op.name == tpnme.raw.STAR then
17401740
if ctx.isJava then
17411741
AppliedTypeTree(ref(defn.RepeatedParamType), t)
17421742
else
17431743
Annotated(
17441744
AppliedTypeTree(ref(defn.SeqType), t),
17451745
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil))
1746-
}
1747-
else {
1746+
else
17481747
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
1749-
if (!enabled(nme.postfixOps)) {
1750-
report.error(
1751-
s"""postfix operator `${op.name}` needs to be enabled
1752-
|by making the implicit value scala.language.postfixOps visible.
1753-
|----
1754-
|This can be achieved by adding the import clause 'import scala.language.postfixOps'
1755-
|or by setting the compiler option -language:postfixOps.
1756-
|See the Scaladoc for value scala.language.postfixOps for a discussion
1757-
|why the feature needs to be explicitly enabled.""".stripMargin, t.srcPos)
1758-
}
17591748
Select(t, op.name)
1760-
}
17611749
case PrefixOp(op, t) =>
17621750
val nspace = if (ctx.mode.is(Mode.Type)) tpnme else nme
17631751
Select(t, nspace.UNARY_PREFIX ++ op.name)

compiler/src/dotty/tools/dotc/config/PathResolver.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object PathResolver {
3030
def ppcp(s: String): String = split(s) match {
3131
case Nil => ""
3232
case Seq(x) => x
33-
case xs => xs map ("\n" + _) mkString
33+
case xs => xs.map("\n" + _).mkString
3434
}
3535

3636
/** Values found solely by inspecting environment or property variables.

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ object Parsers {
5252
enum ParamOwner:
5353
case Class, Type, TypeParam, Def
5454

55+
enum ParseKind:
56+
case Expr, Type, Pattern
57+
5558
type StageKind = Int
5659
object StageKind {
5760
val None = 0
@@ -939,18 +942,19 @@ object Parsers {
939942
def infixOps(
940943
first: Tree, canStartOperand: Token => Boolean, operand: Location => Tree,
941944
location: Location,
942-
isType: Boolean,
943-
isOperator: => Boolean,
944-
maybePostfix: Boolean = false): Tree = {
945+
kind: ParseKind,
946+
isOperator: => Boolean): Tree =
945947
val base = opStack
946948

947949
def recur(top: Tree): Tree =
950+
val isType = kind == ParseKind.Type
948951
if (isIdent && isOperator) {
949-
val op = if (isType) typeIdent() else termIdent()
952+
val op = if isType then typeIdent() else termIdent()
950953
val top1 = reduceStack(base, top, precedence(op.name), !op.name.isRightAssocOperatorName, op.name, isType)
951954
opStack = OpInfo(top1, op, in.offset) :: opStack
952955
colonAtEOLOpt()
953956
newLineOptWhenFollowing(canStartOperand)
957+
val maybePostfix = kind == ParseKind.Expr && in.postfixOpsEnabled
954958
if (maybePostfix && !canStartOperand(in.token)) {
955959
val topInfo = opStack.head
956960
opStack = opStack.tail
@@ -973,7 +977,7 @@ object Parsers {
973977
else top
974978

975979
recur(first)
976-
}
980+
end infixOps
977981

978982
/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */
979983

@@ -1525,8 +1529,7 @@ object Parsers {
15251529
def infixType(): Tree = infixTypeRest(refinedType())
15261530

15271531
def infixTypeRest(t: Tree): Tree =
1528-
infixOps(t, canStartTypeTokens, refinedTypeFn, Location.ElseWhere,
1529-
isType = true,
1532+
infixOps(t, canStartTypeTokens, refinedTypeFn, Location.ElseWhere, ParseKind.Type,
15301533
isOperator = !followingIsVararg())
15311534

15321535
/** RefinedType ::= WithType {[nl] Refinement}
@@ -2218,10 +2221,8 @@ object Parsers {
22182221
t
22192222

22202223
def postfixExprRest(t: Tree, location: Location): Tree =
2221-
infixOps(t, in.canStartExprTokens, prefixExpr, location,
2222-
isType = false,
2223-
isOperator = !(location.inArgs && followingIsVararg()),
2224-
maybePostfix = true)
2224+
infixOps(t, in.canStartExprTokens, prefixExpr, location, ParseKind.Expr,
2225+
isOperator = !(location.inArgs && followingIsVararg()))
22252226

22262227
/** PrefixExpr ::= [PrefixOperator'] SimpleExpr
22272228
* PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’
@@ -2708,8 +2709,8 @@ object Parsers {
27082709
/** InfixPattern ::= SimplePattern {id [nl] SimplePattern}
27092710
*/
27102711
def infixPattern(): Tree =
2711-
infixOps(simplePattern(), in.canStartExprTokens, simplePatternFn, Location.InPattern,
2712-
isType = false,
2712+
infixOps(
2713+
simplePattern(), in.canStartExprTokens, simplePatternFn, Location.InPattern, ParseKind.Pattern,
27132714
isOperator = in.name != nme.raw.BAR && !followingIsVararg())
27142715

27152716
/** SimplePattern ::= PatVar

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ object Scanners {
213213
fewerBracesEnabledCtx = myLanguageImportContext
214214
fewerBracesEnabledCache
215215

216+
private var postfixOpsEnabledCache = false
217+
private var postfixOpsEnabledCtx: Context = NoContext
218+
219+
def postfixOpsEnabled =
220+
if postfixOpsEnabledCtx ne myLanguageImportContext then
221+
postfixOpsEnabledCache = featureEnabled(nme.postfixOps)
222+
postfixOpsEnabledCtx = myLanguageImportContext
223+
postfixOpsEnabledCache
224+
216225
/** All doc comments kept by their end position in a `Map`.
217226
*
218227
* Note: the map is necessary since the comments are looked up after an

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ class CompilationTests {
5858
compileFile("tests/pos-special/kind-projector.scala", defaultOptions.and("-Ykind-projector")),
5959
compileFile("tests/pos-special/kind-projector-underscores.scala", defaultOptions.and("-Ykind-projector:underscores")),
6060
compileFile("tests/run/i5606.scala", defaultOptions.and("-Yretain-trees")),
61-
compileFile("tests/pos-custom-args/i5498-postfixOps.scala", defaultOptions withoutLanguageFeature "postfixOps"),
6261
compileFile("tests/pos-custom-args/i8875.scala", defaultOptions.and("-Xprint:getters")),
6362
compileFile("tests/pos-custom-args/i9267.scala", defaultOptions.and("-Ystop-after:erasure")),
6463
compileFile("tests/pos-special/extend-java-enum.scala", defaultOptions.and("-source", "3.0-migration")),
@@ -180,7 +179,6 @@ class CompilationTests {
180179
compileFile("tests/neg-custom-args/kind-projector.scala", defaultOptions.and("-Ykind-projector")),
181180
compileFile("tests/neg-custom-args/kind-projector-underscores.scala", defaultOptions.and("-Ykind-projector:underscores")),
182181
compileFile("tests/neg-custom-args/typeclass-derivation2.scala", defaultOptions.and("-language:experimental.erasedDefinitions")),
183-
compileFile("tests/neg-custom-args/i5498-postfixOps.scala", defaultOptions withoutLanguageFeature "postfixOps"),
184182
compileFile("tests/neg-custom-args/deptypes.scala", defaultOptions.and("-language:experimental.dependent")),
185183
compileFile("tests/neg-custom-args/matchable.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")),
186184
compileFile("tests/neg-custom-args/i7314.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")),

compiler/test/dotty/tools/repl/TabcompleteTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class TabcompleteTests extends ReplTest {
176176
| case dot_product_*
177177
| case __system
178178
|
179-
|Foo."""stripMargin))
179+
|Foo.""".stripMargin))
180180
}
181181

182182

@@ -192,7 +192,7 @@ class TabcompleteTests extends ReplTest {
192192
| case dot_product_*
193193
| case __system
194194
|
195-
|Foo.`bac"""stripMargin))
195+
|Foo.`bac""".stripMargin))
196196
}
197197

198198
@Test def backtickedImport = initially {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ object TestConfiguration {
6262

6363
val yCheckOptions = Array("-Ycheck:all")
6464

65-
val commonOptions = Array("-indent", "-language:postfixOps") ++ checkOptions ++ noCheckOptions ++ yCheckOptions
65+
val commonOptions = Array("-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions
6666
val defaultOptions = TestFlags(basicClasspath, commonOptions)
6767
val unindentOptions = TestFlags(basicClasspath, Array("-no-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions)
6868
val withCompilerOptions =

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ object Build {
171171
"-unchecked",
172172
"-Xfatal-warnings",
173173
"-encoding", "UTF8",
174-
"-language:existentials,higherKinds,implicitConversions,postfixOps"
174+
"-language:existentials,higherKinds,implicitConversions"
175175
),
176176

177177
(Compile / compile / javacOptions) ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),

scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Parser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ final class Parser(
461461
stack.length > 0 && char != endOfText
462462
}) do {}
463463

464-
list mkString
464+
list.mkString
465465
}
466466

467467
def getInline(isInlineEnd: => Boolean, textTransform: String => String = identity): Inline = {

tests/neg-custom-args/i5498-postfixOps.check

Lines changed: 0 additions & 20 deletions
This file was deleted.

tests/neg/i12361.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.postfixOps
12
object Test {
23
foo = macro Impls . foo [ U ] += // error // error
34
}

tests/neg/i14564.check

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- [E018] Syntax Error: tests/neg/i14564.scala:5:28 --------------------------------------------------------------------
2+
5 |def test = sum"${ List(42)* }" // error // error
3+
| ^
4+
| expression expected but '}' found
5+
|
6+
| longer explanation available when compiling with `-explain`
7+
-- [E008] Not Found Error: tests/neg/i14564.scala:5:26 -----------------------------------------------------------------
8+
5 |def test = sum"${ List(42)* }" // error // error
9+
| ^^^^^^^^^
10+
| value * is not a member of List[Int], but could be made available as an extension method.
11+
|
12+
| One of the following imports might make progress towards fixing the problem:
13+
|
14+
| import math.Fractional.Implicits.infixFractionalOps
15+
| import math.Integral.Implicits.infixIntegralOps
16+
| import math.Numeric.Implicits.infixNumericOps
17+
|

tests/neg/i14564.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import language.postfixOps as _
2+
3+
extension (sc: StringContext) def sum(xs: Int*): String = xs.sum.toString
4+
5+
def test = sum"${ List(42)* }" // error // error
6+

tests/neg/i1672.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
class Test {
33
implicit def compareComparables[T](x: T)(implicit ord: Ordering[T]) = // error: result type of implicit definition needs to be given explicitly
44
new ord.OrderingOps(x)
5-
class Bippy { def compare(y: Bippy) = util Random }
5+
class Bippy { def compare(y: Bippy) = util.Random }
66
() < () // error: value `<` is not a member of Unit
77
}

tests/neg/i1707.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.postfixOps
12
object DepBug {
23
class A {
34
class B

tests/neg/i5498-postfixOps.check

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- [E018] Syntax Error: tests/neg/i5498-postfixOps.scala:4:10 ----------------------------------------------------------
2+
4 | 1 second // error: usage of postfix operator
3+
| ^
4+
| expression expected but end of statement found
5+
|
6+
| longer explanation available when compiling with `-explain`
7+
-- [E018] Syntax Error: tests/neg/i5498-postfixOps.scala:6:37 ----------------------------------------------------------
8+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error
9+
| ^
10+
| expression expected but ')' found
11+
|
12+
| longer explanation available when compiling with `-explain`
13+
-- Error: tests/neg/i5498-postfixOps.scala:6:0 -------------------------------------------------------------------------
14+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error
15+
|^
16+
|no implicit argument of type scala.concurrent.duration.DurationConversions.Classifier[Null] was found for parameter ev of method second in trait DurationConversions

tests/neg-custom-args/i5498-postfixOps.scala renamed to tests/neg/i5498-postfixOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import scala.concurrent.duration.*
33
def test() = {
44
1 second // error: usage of postfix operator
55

6-
Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator
6+
Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error
77
}

tests/neg/i7438.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.postfixOps
12
type Tr[+A]
23
extension [A, B](tr: Tr[A]) inline def map(f: A => B): Tr[B] = ???
34

tests/neg/i9344.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
import language.postfixOps
22
object Test {
33
val I1: Int = 0 * * * 8 * 1 - 1 + 1 + // error
44
}

tests/neg/parser-stability-7.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.postfixOps
12
class x0[x1] {
23
def x2: x1
34
}

tests/neg/t6124.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
import language.postfixOps
22
trait T {
33
def f = 3_14_E-2 // error
44
def e = 3_14E-_2 // error

tests/pos-custom-args/i5498-postfixOps.scala

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/pos/i5498-postfixOps.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import scala.concurrent.duration.*
22

3+
import scala.language.postfixOps
4+
35
def test() = {
4-
// only OK since the defaultOptions in the TestConfiguration includes -language:postfixOps
56
1 second
67

7-
Seq(1, 2).filter(List(1,2) contains)
8-
}
8+
Seq(1, 2) filter (List(1,2) contains) toList
9+
}

tests/pos/test-desugar.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.postfixOps
12
object desugar {
23

34
// variables

tests/run/t8346.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object Test extends App {
2727
inits foreach {case (name, singleton) =>
2828
print(s"${name}: ")
2929
val one = singleton(1)
30-
println(Seq(2,3,4).scanLeft(one)(_ + _) map sVarInfo toList)
30+
println(Seq(2,3,4).scanLeft(one)(_ + _).map(sVarInfo).toList)
3131
}
3232

3333
println(s"ValueSet: ${sVarInfo(SomeEnum.values)}")

0 commit comments

Comments
 (0)