Skip to content

Commit 47760c4

Browse files
committed
Decide on postfix ops in parser
Parse a postfix op only if language.postfixOps is imported. This helps avoiding non-sensical parses. Previously we always parsed, but issued errors in Typer if postfix ops were not enabled.
1 parent 9abe753 commit 47760c4

File tree

5 files changed

+41
-47
lines changed

5 files changed

+41
-47
lines changed

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,28 +1722,16 @@ object desugar {
17221722
// This is a deliberate departure from scalac, where StringContext is not rooted (See #4732)
17231723
Apply(Select(Apply(scalaDot(nme.StringContext), strs), id).withSpan(tree.span), elems)
17241724
case PostfixOp(t, op) =>
1725-
if ((ctx.mode is Mode.Type) && !isBackquoted(op) && op.name == tpnme.raw.STAR) {
1725+
if (ctx.mode is Mode.Type) && !isBackquoted(op) && op.name == tpnme.raw.STAR then
17261726
if ctx.isJava then
17271727
AppliedTypeTree(ref(defn.RepeatedParamType), t)
17281728
else
17291729
Annotated(
17301730
AppliedTypeTree(ref(defn.SeqType), t),
17311731
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil))
1732-
}
1733-
else {
1732+
else
17341733
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
1735-
if (!enabled(nme.postfixOps)) {
1736-
report.error(
1737-
s"""postfix operator `${op.name}` needs to be enabled
1738-
|by making the implicit value scala.language.postfixOps visible.
1739-
|----
1740-
|This can be achieved by adding the import clause 'import scala.language.postfixOps'
1741-
|or by setting the compiler option -language:postfixOps.
1742-
|See the Scaladoc for value scala.language.postfixOps for a discussion
1743-
|why the feature needs to be explicitly enabled.""".stripMargin, t.srcPos)
1744-
}
17451734
Select(t, op.name)
1746-
}
17471735
case PrefixOp(op, t) =>
17481736
val nspace = if (ctx.mode.is(Mode.Type)) tpnme else nme
17491737
Select(t, nspace.UNARY_PREFIX ++ op.name)

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
Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
-- Error: tests/neg-custom-args/i5498-postfixOps.scala:4:2 -------------------------------------------------------------
1+
-- [E018] Syntax Error: tests/neg-custom-args/i5498-postfixOps.scala:4:10 ----------------------------------------------
22
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.
3+
| ^
4+
| expression expected but end of statement found
5+
|
6+
| longer explanation available when compiling with `-explain`
7+
-- [E018] Syntax Error: tests/neg-custom-args/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-custom-args/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

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
}

0 commit comments

Comments
 (0)