Skip to content

Commit 1bcc03c

Browse files
authored
Validate named patterns for case classes (#22242)
I found out that there is no validation happening for named patterns of case classes. https://scastie.scala-lang.org/W4p7RBrySwuteISEPuqSUw There were 2 different things that blocked the errors: 1. We actually did not run `checkWellFormedTupleElems` in that scenario, 2. We run `tryAdaptPatternArgs` in `tryEither` which has nested context that does not report errors which are not sticky.
1 parent ad90f14 commit 1bcc03c

File tree

4 files changed

+44
-10
lines changed

4 files changed

+44
-10
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1667,7 +1667,7 @@ object desugar {
16671667
AppliedTypeTree(
16681668
TypeTree(defn.throwsAlias.typeRef).withSpan(op.span), tpt :: excepts :: Nil)
16691669

1670-
private def checkWellFormedTupleElems(elems: List[Tree])(using Context): List[Tree] =
1670+
def checkWellFormedTupleElems(elems: List[Tree])(using Context): List[Tree] =
16711671
val seen = mutable.Set[Name]()
16721672
for case arg @ NamedArg(name, _) <- elems do
16731673
if seen.contains(name) then

compiler/src/dotty/tools/dotc/typer/Applications.scala

+11-9
Original file line numberDiff line numberDiff line change
@@ -208,20 +208,22 @@ object Applications {
208208
else tp :: Nil
209209

210210
private def productUnapplySelectors(tp: Type)(using Context): Option[List[Type]] =
211+
val validatedTupleElements = desugar.checkWellFormedTupleElems(args)
212+
211213
if defn.isProductSubType(tp) && args.lengthCompare(productArity(tp)) <= 0 then
212-
tryAdaptPatternArgs(args, tp) match
214+
tryAdaptPatternArgs(validatedTupleElements, tp) match
213215
case Some(args1) if isProductMatch(tp, args1.length, pos) =>
214216
args = args1
215217
Some(productSelectorTypes(tp, pos))
216218
case _ => None
217-
else tp.widen.normalized.dealias match
218-
case tp @ defn.NamedTuple(_, tt) =>
219-
tryAdaptPatternArgs(args, tp) match
220-
case Some(args1) =>
221-
args = args1
222-
tt.tupleElementTypes
223-
case _ => None
224-
case _ => None
219+
else tp.widen.normalized.dealias match
220+
case tp @ defn.NamedTuple(_, tt) =>
221+
tryAdaptPatternArgs(validatedTupleElements, tp) match
222+
case Some(args1) =>
223+
args = args1
224+
tt.tupleElementTypes
225+
case _ => None
226+
case _ => None
225227

226228
/** The computed argument types which will be the scutinees of the sub-patterns. */
227229
val argTypes: List[Type] =

tests/neg/named-tuples-4.check

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- Error: tests/neg/named-tuples-4.scala:10:35 -------------------------------------------------------------------------
2+
10 | case PersonCaseClass(name = n, age) => () // error
3+
| ^^^
4+
| Illegal combination of named and unnamed tuple elements
5+
-- Error: tests/neg/named-tuples-4.scala:11:31 -------------------------------------------------------------------------
6+
11 | case PersonCaseClass(name, age = a) => () // error
7+
| ^^^^^^^
8+
| Illegal combination of named and unnamed tuple elements
9+
-- Error: tests/neg/named-tuples-4.scala:15:20 -------------------------------------------------------------------------
10+
15 | case (name = n, age) => () // error
11+
| ^^^
12+
| Illegal combination of named and unnamed tuple elements
13+
-- Error: tests/neg/named-tuples-4.scala:16:16 -------------------------------------------------------------------------
14+
16 | case (name, age = a) => () // error
15+
| ^^^^^^^
16+
| Illegal combination of named and unnamed tuple elements

tests/neg/named-tuples-4.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import language.experimental.namedTuples
2+
import scala.annotation.experimental
3+
4+
@experimental object Test:
5+
6+
case class PersonCaseClass(name: String, age: Int)
7+
8+
val personCaseClass = PersonCaseClass("Bob", 33)
9+
personCaseClass match
10+
case PersonCaseClass(name = n, age) => () // error
11+
case PersonCaseClass(name, age = a) => () // error
12+
13+
val person = (name = "Bob", age = 33): (name: String, age: Int)
14+
person match
15+
case (name = n, age) => () // error
16+
case (name, age = a) => () // error

0 commit comments

Comments
 (0)