diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 5b89c9bbacd1..d121157b223e 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -814,17 +814,31 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case _ => false } - /** An extractor for closures, either contained in a block or standalone. + /** An extractor for closures, possibly typed, and possibly including the + * definition of the ananonymous def. */ object closure { - def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match { - case Block(_, expr) => unapply(expr) - case Closure(env, meth, tpt) => Some(env, meth, tpt) - case Typed(expr, _) => unapply(expr) + def unapply(tree: Tree)(using Context): Option[(List[Tree], Tree, Tree)] = tree match { + case Block((meth : DefDef) :: Nil, closure: Closure) if meth.symbol == closure.meth.symbol => + unapply(closure) + case Block(Nil, expr) => + unapply(expr) + case Closure(env, meth, tpt) => + Some(env, meth, tpt) + case Typed(expr, _) => + unapply(expr) case _ => None } } + /** An extractor for a closure or a block ending in one. This was + * previously `closure` before that one was tightened. + */ + object blockEndingInClosure: + def unapply(tree: Tree)(using Context): Option[(List[Tree], Tree, Tree)] = tree match + case Block(_, expr) => unapply(expr) + case _ => closure.unapply(tree) + /** An extractor for def of a closure contained the block of the closure. */ object closureDef { def unapply(tree: Tree)(using Context): Option[DefDef] = tree match { diff --git a/compiler/src/dotty/tools/dotc/typer/Migrations.scala b/compiler/src/dotty/tools/dotc/typer/Migrations.scala index d6b95ceb93dc..7f27f27112a0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Migrations.scala +++ b/compiler/src/dotty/tools/dotc/typer/Migrations.scala @@ -57,7 +57,7 @@ trait Migrations: val nestedCtx = ctx.fresh.setNewTyperState() val res = typed(qual, pt1)(using nestedCtx) res match { - case closure(_, _, _) => + case blockEndingInClosure(_, _, _) => case _ => val recovered = typed(qual)(using ctx.fresh.setExploreTyperState()) val msg = OnlyFunctionsCanBeFollowedByUnderscore(recovered.tpe.widen, tree) diff --git a/tests/neg-custom-args/captures/i21620.check b/tests/neg-custom-args/captures/i21620.check new file mode 100644 index 000000000000..3a09ba978574 --- /dev/null +++ b/tests/neg-custom-args/captures/i21620.check @@ -0,0 +1,13 @@ +-- [E129] Potential Issue Warning: tests/neg-custom-args/captures/i21620.scala:5:6 ------------------------------------- +5 | x + | ^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21620.scala:9:31 ---------------------------------------- +9 | val _: () -> () ->{x} Unit = f // error + | ^ + | Found: () ->{f} () ->{x} Unit + | Required: () -> () ->{x} Unit + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i21620.scala b/tests/neg-custom-args/captures/i21620.scala new file mode 100644 index 000000000000..a21a41a10863 --- /dev/null +++ b/tests/neg-custom-args/captures/i21620.scala @@ -0,0 +1,10 @@ +class C +def test(x: C^) = + val f = () => + def foo() = + x + () + println(s"hey: $x") + () => foo() + val _: () -> () ->{x} Unit = f // error + () diff --git a/tests/pos-custom-args/captures/i21620.scala b/tests/pos-custom-args/captures/i21620.scala new file mode 100644 index 000000000000..b2c382aa4c75 --- /dev/null +++ b/tests/pos-custom-args/captures/i21620.scala @@ -0,0 +1,11 @@ +class C +def test(x: C^) = + def foo() = + x + () + val f = () => + // println() // uncomenting would give an error, but with + // a different way of handling curried functions should be OK + () => foo() + val _: () -> () ->{x} Unit = f + ()