Skip to content

Fix #7883: Better error message for unapplySeq matches #8583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -327,4 +327,7 @@ abstract class Reporter extends interfaces.ReporterResult {
/** Issue all error messages in this reporter to next outer one, or make sure they are written. */
def flush()(implicit ctx: Context): Unit =
removeBufferedMessages.foreach(ctx.reporter.report)

/** If this reporter buffers messages, all buffered messages, otherwise Nil */
def pendingMessages(using Context): List[MessageContainer] = Nil
}
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ class StoreReporter(outer: Reporter) extends Reporter {
if (infos != null) try infos.toList finally infos = null
else Nil

override def pendingMessages(using Context): List[MessageContainer] = infos.toList

override def errorsReported: Boolean = hasErrors || (outer != null && outer.errorsReported)
}
31 changes: 21 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1066,22 +1066,28 @@ trait Applications extends Compatibility {
if (!tree.tpe.isError && tree.tpe.isErroneous) tree
else errorTree(tree, NotAnExtractor(qual))

/** Does `state` contain a single "NotAMember" message as pending error message
* that says `$membername is not a member of ...` ?
*/
def saysNotAMember(state: TyperState, memberName: TermName): Boolean =
state.reporter.pendingMessages match
case msg :: Nil =>
msg.contained match
case NotAMember(_, name, _, _) => name == memberName
case _ => false
case _ => false

/** 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.
*/
def reportErrors(tree: Tree, state: TyperState): Tree =
assert(state.reporter.hasErrors)
val msgs = state.reporter.removeBufferedMessages
msgs match
case msg :: Nil =>
msg.contained match
case NotAMember(_, nme.unapply, _, _) => return notAnExtractor(tree)
case _ =>
case _ =>
msgs.foreach(ctx.reporter.report)
tree
if saysNotAMember(state, nme.unapply) then notAnExtractor(tree)
else
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
Expand Down Expand Up @@ -1145,7 +1151,12 @@ trait Applications extends Compatibility {
tryWithName(nme.unapply) {
(sel, state) =>
tryWithName(nme.unapplySeq) {
(_, _) => fallBack(sel, state)
(sel2, state2) =>
// if both fail, return unapply error, unless that is simply a
// "not a member", and the unapplySeq error is more refined.
if saysNotAMember(state, nme.unapply) && !saysNotAMember(state2, nme.unapplySeq)
then fallBack(sel2, state2)
else fallBack(sel, state)
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions tests/neg-custom-args/explicit-nulls/i7883.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- [E134] Type Mismatch Error: tests/neg-custom-args/explicit-nulls/i7883.scala:6:11 -----------------------------------
6 | case r(hd, tl) => Some((hd, tl)) // error // error // error
| ^
| None of the overloaded alternatives of method unapplySeq in class Regex with types
| (m: scala.util.matching.Regex.Match): Option[List[String]]
| (c: Char): Option[List[Char]]
| (s: CharSequence): Option[List[String]]
| match arguments (String | UncheckedNull)
-- [E006] Unbound Identifier Error: tests/neg-custom-args/explicit-nulls/i7883.scala:6:30 ------------------------------
6 | case r(hd, tl) => Some((hd, tl)) // error // error // error
| ^^
| Not found: hd

longer explanation available when compiling with `-explain`
-- [E006] Unbound Identifier Error: tests/neg-custom-args/explicit-nulls/i7883.scala:6:34 ------------------------------
6 | case r(hd, tl) => Some((hd, tl)) // error // error // error
| ^^
| Not found: tl

longer explanation available when compiling with `-explain`
9 changes: 9 additions & 0 deletions tests/neg-custom-args/explicit-nulls/i7883.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.util.matching.Regex

object Test extends App {
def head(s: String, r: Regex): Option[(String, String)] =
s.trim match {
case r(hd, tl) => Some((hd, tl)) // error // error // error
case _ => None
}
}