From 7c308d6a7a3cef4ea804623e821250065cfae444 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 19 Oct 2023 10:22:39 +0200 Subject: [PATCH 1/4] Better error message when a pattern match extractor is not found. Fixes #18684 --- .../dotty/tools/dotc/core/TypeErrors.scala | 14 +++++---- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 12 ++++++-- .../dotty/tools/dotc/typer/Applications.scala | 19 ++++++++---- .../src/dotty/tools/dotc/typer/Typer.scala | 24 +++++++++++---- tests/neg/bad-unapplies.check | 8 ++--- tests/neg/i18684.check | 30 +++++++++++++++++++ tests/neg/i18684.scala | 12 ++++++++ tests/neg/i5101.check | 4 +-- tests/neg/t5702-neg-bad-and-wild.check | 8 ++--- 10 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 tests/neg/i18684.check create mode 100644 tests/neg/i18684.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 87e4ba923e58..dcc4874bc34f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -134,14 +134,18 @@ end handleRecursive * so it requires knowing denot already. * @param denot */ -class CyclicReference private (val denot: SymDenotation)(using Context) extends TypeError: +class CyclicReference(val denot: SymDenotation)(using Context) extends TypeError: var inImplicitSearch: Boolean = false - override def toMessage(using Context): Message = - val cycleSym = denot.symbol + val cycleSym = denot.symbol + + // cycleSym.flags would try completing denot and would fail, but here we can use flagsUNSAFE to detect flags + // set by the parser. + def unsafeFlags = cycleSym.flagsUNSAFE + def isMethod = unsafeFlags.is(Method) + def isVal = !isMethod && cycleSym.isTerm - // cycleSym.flags would try completing denot and would fail, but here we can use flagsUNSAFE to detect flags - // set by the parser. + override def toMessage(using Context): Message = val unsafeFlags = cycleSym.flagsUNSAFE val isMethod = unsafeFlags.is(Method) val isVal = !isMethod && cycleSym.isTerm diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 5cbe336acc28..77989227b1a9 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -202,6 +202,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case ImplausiblePatternWarningID // erorNumber: 186 case SynchronizedCallOnBoxedClassID // errorNumber: 187 case VarArgsParamCannotBeGivenID // erorNumber: 188 + case ExtractorNotFoundID // errorNumber: 189 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 673e3dcc243d..853395d5b122 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -876,7 +876,7 @@ extends Message(PatternMatchExhaustivityID) { val pathes = List( ActionPatch( - srcPos = endPos, + srcPos = endPos, replacement = uncoveredCases.map(c => indent(s"case $c => ???", startColumn)) .mkString("\n", "\n", "") ), @@ -2358,7 +2358,7 @@ class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends def explain(using Context) = "" } -class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { +class NotAnExtractor(tree: untpd.Tree)(using Context) extends PatternMatchMsg(NotAnExtractorID) { def msg(using Context) = i"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" def explain(using Context) = i"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: @@ -2371,6 +2371,14 @@ class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExt |This mechanism is used for instance in pattern ${hl("case List(x1, ..., xn)")}""" } +class ExtractorNotFound(val name: Name)(using Context) extends NotFoundMsg(ExtractorNotFoundID): + def msg(using Context) = i"no pattern match extractor named $name was found" + def explain(using Context) = + i"""An application $name(...) in a pattern can refer to an extractor + |which defines an unapply or unapplySeq method. Case classes and enum cases + |implicitly define extractors with the name of the class or enum case. + |Here, no extractor named $name was found, so the pattern could not be typed.""" + class MemberWithSameNameAsStatic()(using Context) extends SyntaxMsg(MemberWithSameNameAsStaticID) { def msg(using Context) = i"Companion classes cannot define members with same name as a ${hl("@static")} member" diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 9501e51aeb6f..e6ff916cc672 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1295,15 +1295,22 @@ trait Applications extends Compatibility { /** Report errors buffered in state. * @pre state has errors to report - * If there is a single error stating that "unapply" is not a member, print - * the more informative "notAnExtractor" message instead. + * If the last reported error states that "unapply" is not a member, report + * the more informative `NotAnExtractor` message instead. + * If the last reported error states that the qualifier was not found, report + * the more informative `ExtractorNotFound` message instead. */ def reportErrors(tree: Tree, state: TyperState): Tree = assert(state.reporter.hasErrors) - if saysNotFound(state, nme.unapply) then notAnExtractor(tree) - else - state.reporter.flush() - tree + if saysNotFound(state, nme.unapply) then + notAnExtractor(tree) + else qual match + case qual: Ident if saysNotFound(state, qual.name) => + report.error(ExtractorNotFound(qual.name), tree.srcPos) + tree + case _ => + state.reporter.flush() + tree /** If this is a term ref tree, try to typecheck with its type name. * If this refers to a type alias, follow the alias, and if diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 431e863f85d2..82bd4e4d479b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3172,6 +3172,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => typedUnadapted(desugar(tree, pt), pt, locked) } + def handleTypeError(ex: TypeError): Tree = ex match + case ex: CyclicReference + if ctx.reporter.errorsReported + && xtree.span.isZeroExtent + && ex.isVal && false => + // Don't report a "recursive val ... needs type" if errors were reported + // previously and the span of the offending tree is empty. In this case, + // it's most likely that this is desugared code, and the error message would + // be redundant and confusing. + xtree.withType(ErrorType(ex.toMessage)) + case _ => + // Use focussed sourcePos since tree might be a large definition + // and a large error span would hide all errors in interior. + // TODO: Not clear that hiding is what we want, actually + errorTree(xtree, ex, xtree.srcPos.focus) + try val ifpt = defn.asContextFunctionType(pt) val result = @@ -3194,11 +3210,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer result.tpe.stripTypeVar match case e: ErrorType if !unsimplifiedType.isErroneous => errorTree(xtree, e.msg, xtree.srcPos) case _ => result - catch case ex: TypeError => errorTree(xtree, ex, xtree.srcPos.focus) - // use focussed sourcePos since tree might be a large definition - // and a large error span would hide all errors in interior. - // TODO: Not clear that hiding is what we want, actually - } + catch case ex: TypeError => + handleTypeError(ex) + } } /** Interpolate and simplify the type of the given tree. */ diff --git a/tests/neg/bad-unapplies.check b/tests/neg/bad-unapplies.check index 44633ca6950a..51e71d8e8949 100644 --- a/tests/neg/bad-unapplies.check +++ b/tests/neg/bad-unapplies.check @@ -7,13 +7,13 @@ | both match arguments (C) | | longer explanation available when compiling with `-explain` --- [E127] Syntax Error: tests/neg/bad-unapplies.scala:23:9 ------------------------------------------------------------- +-- [E127] Pattern Match Error: tests/neg/bad-unapplies.scala:23:9 ------------------------------------------------------ 23 | case B("2") => // error (cannot be used as an extractor) | ^ | B cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method | | longer explanation available when compiling with `-explain` --- [E127] Syntax Error: tests/neg/bad-unapplies.scala:24:9 ------------------------------------------------------------- +-- [E127] Pattern Match Error: tests/neg/bad-unapplies.scala:24:9 ------------------------------------------------------ 24 | case D("2") => // error (cannot be used as an extractor) | ^ | D cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method @@ -31,9 +31,9 @@ | Wrong number of argument patterns for F; expected: () | | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/bad-unapplies.scala:27:9 ---------------------------------------------------------- +-- [E189] Not Found Error: tests/neg/bad-unapplies.scala:27:9 ---------------------------------------------------------- 27 | case G("2") => // error (Not found: G) | ^ - | Not found: G + | no pattern match extractor named G was found | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check new file mode 100644 index 000000000000..69ed47cc2613 --- /dev/null +++ b/tests/neg/i18684.check @@ -0,0 +1,30 @@ +-- [E189] Not Found Error: tests/neg/i18684.scala:3:6 ------------------------------------------------------------------ +3 | val s(): String = "hello, world" // error + | ^ + | no pattern match extractor named s was found + | + | longer explanation available when compiling with `-explain` +-- [E189] Not Found Error: tests/neg/i18684.scala:5:6 ------------------------------------------------------------------ +5 | val i() = 22 // error + | ^ + | no pattern match extractor named i was found + | + | longer explanation available when compiling with `-explain` +-- [E189] Not Found Error: tests/neg/i18684.scala:10:8 ----------------------------------------------------------------- +10 | val foo() = "33" // error + | ^^^ + | no pattern match extractor named foo was found + | + | longer explanation available when compiling with `-explain` +-- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- +12 | val inner(x) = 3 // error // error + | ^^^^^ + | Test.inner cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method + | + | longer explanation available when compiling with `-explain` +-- [E045] Cyclic Error: tests/neg/i18684.scala:12:14 ------------------------------------------------------------------- +12 | val inner(x) = 3 // error // error + | ^ + | Recursive value x needs type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i18684.scala b/tests/neg/i18684.scala new file mode 100644 index 000000000000..48e2d691a304 --- /dev/null +++ b/tests/neg/i18684.scala @@ -0,0 +1,12 @@ +//> using option -explain +object Test: + val s(): String = "hello, world" // error + + val i() = 22 // error + + def foo(): String = "22" + + object inner: + val foo() = "33" // error + + val inner(x) = 3 // error // error \ No newline at end of file diff --git a/tests/neg/i5101.check b/tests/neg/i5101.check index 4f4bac89aa44..c86976398b0e 100644 --- a/tests/neg/i5101.check +++ b/tests/neg/i5101.check @@ -1,6 +1,6 @@ --- [E006] Not Found Error: tests/neg/i5101.scala:11:11 ----------------------------------------------------------------- +-- [E189] Not Found Error: tests/neg/i5101.scala:11:11 ----------------------------------------------------------------- 11 | case A0(_) => // error | ^^ - | Not found: A0 + | no pattern match extractor named A0 was found | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index c43ca54438d3..3be95d3f7666 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -46,16 +46,16 @@ | x is already defined as value x | | Note that overloaded methods must all be defined in the same group of toplevel definitions --- [E006] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:12:20 ------------------------------------------------ +-- [E189] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:12:20 ------------------------------------------------ 12 | case List(1, _*3,) => // error: pattern expected // error | ^ - | Not found: * + | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:13:20 ------------------------------------------------ +-- [E189] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:13:20 ------------------------------------------------ 13 | case List(1, _*3:) => // error // error | ^ - | Not found: * + | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` -- [E045] Cyclic Error: tests/neg/t5702-neg-bad-and-wild.scala:23:19 --------------------------------------------------- From aa0df6c094149ea9d21bf3e96626f27583278ee4 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 19 Oct 2023 11:13:54 +0200 Subject: [PATCH 2/4] Suppress redundant "recursive value needs type" messages --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/neg-macros/i6997b.scala | 2 +- tests/neg/i18020.scala | 2 +- tests/neg/i18684.check | 8 +------- tests/neg/i18684.scala | 2 +- tests/neg/t5702-neg-bad-and-wild.check | 8 +------- tests/neg/t5702-neg-bad-and-wild.scala | 2 +- 7 files changed, 7 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 82bd4e4d479b..d057ee375e2d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3176,7 +3176,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case ex: CyclicReference if ctx.reporter.errorsReported && xtree.span.isZeroExtent - && ex.isVal && false => + && ex.isVal => // Don't report a "recursive val ... needs type" if errors were reported // previously and the span of the offending tree is empty. In this case, // it's most likely that this is desugared code, and the error message would diff --git a/tests/neg-macros/i6997b.scala b/tests/neg-macros/i6997b.scala index fe4f0dd638f5..0ba517d97582 100644 --- a/tests/neg-macros/i6997b.scala +++ b/tests/neg-macros/i6997b.scala @@ -5,7 +5,7 @@ import scala.quoted.* inline def mcr(x: => Any): Any = ${mcrImpl('x)} def mcrImpl(body: Expr[Any])(using ctx: Quotes): Expr[Any] = { - val '{$x: $t} = body // error // error + val '{$x: $t} = body // error '{ val tmp: $t = $x.asInstanceOf[$t] // error // error println(tmp) diff --git a/tests/neg/i18020.scala b/tests/neg/i18020.scala index 2714574af5fc..4062aa9e16f7 100644 --- a/tests/neg/i18020.scala +++ b/tests/neg/i18020.scala @@ -24,7 +24,7 @@ def foo1: Unit = // then Typer rejects "String" as an infix extractor (like ::) // which is the second error -def foo2: Unit = // error +def foo2: Unit = // was: error, recursive value _root_ needs type val _root_ : String = "abc" // error // i17757 diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check index 69ed47cc2613..c384570ff798 100644 --- a/tests/neg/i18684.check +++ b/tests/neg/i18684.check @@ -17,14 +17,8 @@ | | longer explanation available when compiling with `-explain` -- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- -12 | val inner(x) = 3 // error // error +12 | val inner(x) = 3 // error | ^^^^^ | Test.inner cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method | | longer explanation available when compiling with `-explain` --- [E045] Cyclic Error: tests/neg/i18684.scala:12:14 ------------------------------------------------------------------- -12 | val inner(x) = 3 // error // error - | ^ - | Recursive value x needs type - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i18684.scala b/tests/neg/i18684.scala index 48e2d691a304..6ddca460814c 100644 --- a/tests/neg/i18684.scala +++ b/tests/neg/i18684.scala @@ -9,4 +9,4 @@ object Test: object inner: val foo() = "33" // error - val inner(x) = 3 // error // error \ No newline at end of file + val inner(x) = 3 // error \ No newline at end of file diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 3be95d3f7666..c06e2a5fb70a 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -31,7 +31,7 @@ | | longer explanation available when compiling with `-explain` -- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:23:17 --------------------------------------------------- -23 | val K(ns @ _*, xx) = k // error: pattern expected // error +23 | val K(ns @ _*, xx) = k // error: pattern expected | ^ | pattern expected | @@ -58,12 +58,6 @@ | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` --- [E045] Cyclic Error: tests/neg/t5702-neg-bad-and-wild.scala:23:19 --------------------------------------------------- -23 | val K(ns @ _*, xx) = k // error: pattern expected // error - | ^ - | Recursive value $1$ needs type - | - | longer explanation available when compiling with `-explain` -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- 13 | case List(1, _*3:) => // error // error | ^ diff --git a/tests/neg/t5702-neg-bad-and-wild.scala b/tests/neg/t5702-neg-bad-and-wild.scala index 8a4f51962ff2..6031fee93bfc 100644 --- a/tests/neg/t5702-neg-bad-and-wild.scala +++ b/tests/neg/t5702-neg-bad-and-wild.scala @@ -20,7 +20,7 @@ object Test { // good syntax, bad semantics, detected by typer //gowild.scala:14: error: star patterns must correspond with varargs parameters val K(x @ _*) = k - val K(ns @ _*, xx) = k // error: pattern expected // error + val K(ns @ _*, xx) = k // error: pattern expected val K(x) = k // error: x is already defined as value x val (b, _ * ) = (5,6) // error: bad use of `*` // no longer complains From 8df6b48bb5e068975182c66806d715a09e3c82ba Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 19 Oct 2023 11:22:01 +0200 Subject: [PATCH 3/4] Turn on -explain in test --- tests/neg/i18684.check | 42 +++++++++++++++++++++++++++++++++++------- tests/neg/i18684.scala | 2 +- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check index c384570ff798..f3065fa059b7 100644 --- a/tests/neg/i18684.check +++ b/tests/neg/i18684.check @@ -2,23 +2,51 @@ 3 | val s(): String = "hello, world" // error | ^ | no pattern match extractor named s was found - | - | longer explanation available when compiling with `-explain` + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An application s(...) in a pattern can refer to an extractor + | which defines an unapply or unapplySeq method. Case classes and enum cases + | implicitly define extractors with the name of the class or enum case. + | Here, no extractor named s was found, so the pattern could not be typed. + --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:5:6 ------------------------------------------------------------------ 5 | val i() = 22 // error | ^ | no pattern match extractor named i was found - | - | longer explanation available when compiling with `-explain` + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An application i(...) in a pattern can refer to an extractor + | which defines an unapply or unapplySeq method. Case classes and enum cases + | implicitly define extractors with the name of the class or enum case. + | Here, no extractor named i was found, so the pattern could not be typed. + --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:10:8 ----------------------------------------------------------------- 10 | val foo() = "33" // error | ^^^ | no pattern match extractor named foo was found - | - | longer explanation available when compiling with `-explain` + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An application foo(...) in a pattern can refer to an extractor + | which defines an unapply or unapplySeq method. Case classes and enum cases + | implicitly define extractors with the name of the class or enum case. + | Here, no extractor named foo was found, so the pattern could not be typed. + -------------------------------------------------------------------------------------------------------------------- -- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- 12 | val inner(x) = 3 // error | ^^^^^ | Test.inner cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An unapply method should be defined in an object as follow: + | - If it is just a test, return a Boolean. For example case even() + | - If it returns a single sub-value of type T, return an Option[T] + | - If it returns several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)] | - | longer explanation available when compiling with `-explain` + | Sometimes, the number of sub-values isn't fixed and we would like to return a sequence. + | For this reason, you can also define patterns through unapplySeq which returns Option[Seq[T]]. + | This mechanism is used for instance in pattern case List(x1, ..., xn) + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i18684.scala b/tests/neg/i18684.scala index 6ddca460814c..a6d27a1b3d01 100644 --- a/tests/neg/i18684.scala +++ b/tests/neg/i18684.scala @@ -1,4 +1,4 @@ -//> using option -explain +//> using options -explain object Test: val s(): String = "hello, world" // error From eed38ec61de24952751eb66d4b456bc317ed314f Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 19 Oct 2023 16:01:06 +0200 Subject: [PATCH 4/4] Add example to explanation --- .../dotty/tools/dotc/reporting/messages.scala | 14 ++++++- tests/neg/i18684.check | 42 ++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 853395d5b122..9dda0264f7f8 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2375,8 +2375,18 @@ class ExtractorNotFound(val name: Name)(using Context) extends NotFoundMsg(Extra def msg(using Context) = i"no pattern match extractor named $name was found" def explain(using Context) = i"""An application $name(...) in a pattern can refer to an extractor - |which defines an unapply or unapplySeq method. Case classes and enum cases - |implicitly define extractors with the name of the class or enum case. + |which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + |The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + |`snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + |enum cases implicitly define extractors with the name of the class or enum case. |Here, no extractor named $name was found, so the pattern could not be typed.""" class MemberWithSameNameAsStatic()(using Context) diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check index f3065fa059b7..5dc4a2fdd736 100644 --- a/tests/neg/i18684.check +++ b/tests/neg/i18684.check @@ -6,8 +6,18 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | An application s(...) in a pattern can refer to an extractor - | which defines an unapply or unapplySeq method. Case classes and enum cases - | implicitly define extractors with the name of the class or enum case. + | which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + | The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + | `snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + | enum cases implicitly define extractors with the name of the class or enum case. | Here, no extractor named s was found, so the pattern could not be typed. --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:5:6 ------------------------------------------------------------------ @@ -18,8 +28,18 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | An application i(...) in a pattern can refer to an extractor - | which defines an unapply or unapplySeq method. Case classes and enum cases - | implicitly define extractors with the name of the class or enum case. + | which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + | The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + | `snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + | enum cases implicitly define extractors with the name of the class or enum case. | Here, no extractor named i was found, so the pattern could not be typed. --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:10:8 ----------------------------------------------------------------- @@ -30,8 +50,18 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | An application foo(...) in a pattern can refer to an extractor - | which defines an unapply or unapplySeq method. Case classes and enum cases - | implicitly define extractors with the name of the class or enum case. + | which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + | The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + | `snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + | enum cases implicitly define extractors with the name of the class or enum case. | Here, no extractor named foo was found, so the pattern could not be typed. -------------------------------------------------------------------------------------------------------------------- -- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 -------------------------------------------------------------