diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index aedf4cd618f8..8c7ddb1391b0 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -51,7 +51,7 @@ object Parsers { case InGuard extends Location(false, false, false) case InPatternArgs extends Location(false, true, true) // InParens not true, since it might be an alternative case InBlock extends Location(false, false, false) - case ElseWhere extends Location(false, false, false) + case Elsewhere extends Location(false, false, false) enum ParamOwner: case Class // class or trait or enum @@ -1914,7 +1914,7 @@ object Parsers { def infixType(inContextBound: Boolean = false): Tree = infixTypeRest(inContextBound)(refinedType()) def infixTypeRest(inContextBound: Boolean = false)(t: Tree, operand: Location => Tree = refinedTypeFn): Tree = - infixOps(t, canStartInfixTypeTokens, operand, Location.ElseWhere, ParseKind.Type, + infixOps(t, canStartInfixTypeTokens, operand, Location.Elsewhere, ParseKind.Type, isOperator = !followingIsVararg() && !isPureArrow && !(isIdent(nme.as) && sourceVersion.enablesNewGivens && inContextBound) @@ -2402,9 +2402,9 @@ object Parsers { inSepRegion(InCond): expr1Rest( postfixExprRest( - simpleExprRest(t, Location.ElseWhere), - Location.ElseWhere), - Location.ElseWhere) + simpleExprRest(t, Location.Elsewhere), + Location.Elsewhere), + Location.Elsewhere) else if rewriteToNewSyntax(t.span) then dropParensOrBraces(t.span.start, tokenString(altToken)) @@ -2454,7 +2454,7 @@ object Parsers { */ val exprInParens: () => Tree = () => expr(Location.InParens) - val expr: () => Tree = () => expr(Location.ElseWhere) + val expr: () => Tree = () => expr(Location.Elsewhere) def subExpr() = subPart(expr) @@ -2499,7 +2499,7 @@ object Parsers { wrapPlaceholders(t) } - def expr1(location: Location = Location.ElseWhere): Tree = in.token match + def expr1(location: Location = Location.Elsewhere): Tree = in.token match case IF => ifExpr(in.offset, If) case WHILE => @@ -2604,7 +2604,7 @@ object Parsers { t match case Ident(_) | Select(_, _) | Apply(_, _) | PrefixOp(_, _) | PostfixOp(_, _) => atSpan(startOffset(t), in.skipToken()) { - val loc = if location.inArgs then location else Location.ElseWhere + val loc = if location.inArgs then location else Location.Elsewhere Assign(t, subPart(() => expr(loc))) } case _ => @@ -2794,7 +2794,7 @@ object Parsers { * | InfixExpr id ColonArgument * | InfixExpr MatchClause */ - def postfixExpr(location: Location = Location.ElseWhere): Tree = + def postfixExpr(location: Location = Location.Elsewhere): Tree = val t = postfixExprRest(prefixExpr(location), location) if location.inArgs && followingIsVararg() then Typed(t, atSpan(in.skipToken()) { Ident(tpnme.WILDCARD_STAR) }) @@ -2876,7 +2876,7 @@ object Parsers { newExpr() case MACRO => val start = in.skipToken() - MacroTree(simpleExpr(Location.ElseWhere)) + MacroTree(simpleExpr(Location.Elsewhere)) case _ => if isLiteral then literal() @@ -3314,30 +3314,46 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil + // After a pattern, accept colon and type or ascription per tree. + // Warn if old style ascription after pattern that is not a simple name. + // Warn mildly for case X: String, that is, introducing a "constant" id in a typed pattern. + def checkedAscription(pat: Tree, inPattern: Boolean = true)(tree: => Tree): Tree = + val atColon = in.isColon + tree.tap: tree => + if atColon then + val isIdent = unsplice(pat) match { + case x: Ident => + if inPattern && !x.name.isVarPattern then + val tpt = tree match + case Typed(_, tpt) => i"${tpt}" + case _ => "T" + report.warning(em"Typed pattern is not a variable pattern but could be written `${x.name} @ (_: $tpt)`", + pat.sourcePos) + true + case _ => false + } + if !isIdent && !pat.isInstanceOf[Number] then + report.errorOrMigrationWarning( + em"""Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + |are no longer supported. Remove the type ascription or move it to a separate variable pattern.""", + pat.sourcePos, + MigrationVersion.AscriptionAfterPattern) + /** Pattern1 ::= PatVar `:` RefinedType * | [‘-’] integerLiteral `:` RefinedType * | [‘-’] floatingPointLiteral `:` RefinedType * | Pattern2 */ def pattern1(location: Location = Location.InPattern): Tree = - val p = pattern2(location) - if in.isColon then - val isVariable = unsplice(p) match { - case x: Ident => x.name.isVarPattern - case _ => false - } - val isVariableOrNumber = isVariable || p.isInstanceOf[Number] - if !isVariableOrNumber then - report.errorOrMigrationWarning( - em"""Type ascriptions after patterns other than: - | * variable pattern, e.g. `case x: String =>` - | * number literal pattern, e.g. `case 10.5: Double =>` - |are no longer supported. Remove the type ascription or move it to a separate variable pattern.""", - p.sourcePos, - MigrationVersion.AscriptionAfterPattern) - in.nextToken() - ascription(p, location) - else p + val pat = pattern2(location) + inline def maybeAscription = + if in.isColon then + in.nextToken() + ascription(pat, location) + else pat + checkedAscription(pat, inPattern = true)(maybeAscription) /** Pattern3 ::= InfixPattern */ @@ -4108,7 +4124,7 @@ object Parsers { case _ => first :: Nil } - val tpt = typedOpt() + val tpt = checkedAscription(first, inPattern = false)(typedOpt()) val rhs = if tpt.isEmpty || in.token == EQUALS then accept(EQUALS) diff --git a/tests/neg/i15784.check b/tests/neg/i15784.check index d8c9c193c7dc..c1540b33d956 100644 --- a/tests/neg/i15784.check +++ b/tests/neg/i15784.check @@ -10,17 +10,3 @@ | Not found: A | | longer explanation available when compiling with `-explain` --- Warning: tests/neg/i15784.scala:7:7 --------------------------------------------------------------------------------- -7 | case X: Int => X // warn - | ^ - | Type ascriptions after patterns other than: - | * variable pattern, e.g. `case x: String =>` - | * number literal pattern, e.g. `case 10.5: Double =>` - | are no longer supported. Remove the type ascription or move it to a separate variable pattern. --- Warning: tests/neg/i15784.scala:10:7 -------------------------------------------------------------------------------- -10 | case `Int`: Int => `Int` // warn - | ^^^^^ - | Type ascriptions after patterns other than: - | * variable pattern, e.g. `case x: String =>` - | * number literal pattern, e.g. `case 10.5: Double =>` - | are no longer supported. Remove the type ascription or move it to a separate variable pattern. diff --git a/tests/neg/i15784.scala b/tests/neg/i15784.scala index 4be909198eaa..edf69d32f95a 100644 --- a/tests/neg/i15784.scala +++ b/tests/neg/i15784.scala @@ -2,9 +2,3 @@ def i15784 = List(42) match case List(_, Rest @ `a`) => Rest // error case List(_, Rest @ A) => Rest // error case _ => ??? - -def case2 = 42 match - case X: Int => X // warn - -def case3 = 42 match - case `Int`: Int => `Int` // warn diff --git a/tests/neg/i25595.check b/tests/neg/i25595.check new file mode 100644 index 000000000000..5c3255ecc740 --- /dev/null +++ b/tests/neg/i25595.check @@ -0,0 +1,7 @@ +-- Error: tests/neg/i25595.scala:5:4 ----------------------------------------------------------------------------------- +5 |val ((x1, f1), (x2, f2)): ( // error + | ^^^^^^^^^^^^^^^^^^^^ + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. diff --git a/tests/neg/i25595.scala b/tests/neg/i25595.scala new file mode 100644 index 000000000000..7f99ec96634b --- /dev/null +++ b/tests/neg/i25595.scala @@ -0,0 +1,8 @@ +//> using options -source:future + +val avs = ((0d, BigDecimal(1)), (1d, BigDecimal(2))) + +val ((x1, f1), (x2, f2)): ( // error + (Double, BigDecimal), + (Double, BigDecimal) +) = avs diff --git a/tests/warn/i25004/test_2.scala b/tests/warn/i25004/test_2.scala index 5b22b772421f..5dc65537523a 100644 --- a/tests/warn/i25004/test_2.scala +++ b/tests/warn/i25004/test_2.scala @@ -1,6 +1,6 @@ //> using options -Werror -Wunused:all -Xcheck-macros @main def Test = TestBuilder.test: - val start @ _: String = "" // Converting this to a match expression resolves the error + val start @ (_: String) = "" // Converting this to a match expression resolves the error // Alternative: val Seq(start) = Seq("") print(start) diff --git a/tests/warn/i25004b/test_2.scala b/tests/warn/i25004b/test_2.scala index baef613c2b47..bf6944fbd618 100644 --- a/tests/warn/i25004b/test_2.scala +++ b/tests/warn/i25004b/test_2.scala @@ -1,6 +1,6 @@ //> using options -Werror -Wunused:all @main def Test = TestBuilder.test: - val start @ _: String = "" // Converting this to a match expression resolves the error + val start @ (_: String) = "" // Converting this to a match expression resolves the error // Alternative: val Seq(start) = Seq("") print(start) diff --git a/tests/warn/i25595.check b/tests/warn/i25595.check new file mode 100644 index 000000000000..13d9f037af25 --- /dev/null +++ b/tests/warn/i25595.check @@ -0,0 +1,15 @@ +-- Warning: tests/warn/i25595.scala:3:4 -------------------------------------------------------------------------------- +3 |val ((x1, f1), (x2, f2)): ( // warn + | ^^^^^^^^^^^^^^^^^^^^ + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. +-- Warning: tests/warn/i25595.scala:9:7 -------------------------------------------------------------------------------- +9 | case X: Int => X // warn + | ^ + | Typed pattern is not a variable pattern but could be written `X @ (_: Int)` +-- Warning: tests/warn/i25595.scala:12:7 ------------------------------------------------------------------------------- +12 | case `Int`: Int => `Int` // warn + | ^^^^^ + | Typed pattern is not a variable pattern but could be written `Int @ (_: Int)` diff --git a/tests/warn/i25595.scala b/tests/warn/i25595.scala new file mode 100644 index 000000000000..5fce45a97c1b --- /dev/null +++ b/tests/warn/i25595.scala @@ -0,0 +1,12 @@ +val avs = ((0d, BigDecimal(1)), (1d, BigDecimal(2))) + +val ((x1, f1), (x2, f2)): ( // warn + (Double, BigDecimal), + (Double, BigDecimal) +) = avs + +def notVar(i: Int) = i match + case X: Int => X // warn + +def notVarBackquoted(i: Int) = i match + case `Int`: Int => `Int` // warn