diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index d31c98761a20..277ab6a2a554 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit d31c98761a204c91f0e06a4eaa8a45aa038d14b8 +Subproject commit 277ab6a2a554436238caef2c6d8b660ae4dc1e7e diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 79e4a2251f4d..e54725f48ff4 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -80,7 +80,7 @@ class Compiler { new ExplicitOuter, // Add accessors to outer classes from nested ones. new ExplicitSelf, // Make references to non-trivial self types explicit as casts new ElimByName, // Expand by-name parameter references - new StringInterpolatorOpt) :: // Optimizes raw and s string interpolators by rewriting them to string concatentations + new StringInterpolatorOpt) :: // Optimizes raw and s string interpolators by rewriting them to string concatenations List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions new InlinePatterns, // Remove placeholders of inlined patterns new VCInlineMethods, // Inlines calls to value class methods diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e0053f0f8aff..c515ca94c93c 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1306,9 +1306,10 @@ object desugar { * def $anonfun(params) = body * Closure($anonfun) */ - def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = null, isContextual: Boolean)(using Context): Block = + def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = null, isContextual: Boolean, span: Span)(using Context): Block = Block( DefDef(nme.ANON_FUN, params :: Nil, if (tpt == null) TypeTree() else tpt, body) + .withSpan(span) .withMods(synthetic | Artifact), Closure(Nil, Ident(nme.ANON_FUN), if (isContextual) ContextualEmptyTree else EmptyTree)) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index a608852c9b20..16fcf9d25ed3 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -329,6 +329,11 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] def isFunctionWithUnknownParamType(tree: Tree): Boolean = functionWithUnknownParamType(tree).isDefined + def isFunction(tree: Tree): Boolean = tree match + case Function(_, _) | Match(EmptyTree, _) => true + case Block(Nil, expr) => isFunction(expr) + case _ => false + /** Is `tree` an context function or closure, possibly nested in a block? */ def isContextualClosure(tree: Tree)(using Context): Boolean = unsplice(tree) match { case tree: FunctionWithMods => tree.mods.is(Given) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 4ad7646ade69..a87a3dfe50a6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -43,6 +43,14 @@ object ErrorReporting { def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree], pos: SrcPos)(using Context): ErrorType = errorType(WrongNumberOfTypeArgs(fntpe, expectedArgs, actual), pos) + def missingArgs(tree: Tree, mt: Type)(using Context): Unit = + val meth = err.exprStr(methPart(tree)) + mt match + case mt: MethodType if mt.paramNames.isEmpty => + report.error(MissingEmptyArgumentList(meth), tree.srcPos) + case _ => + report.error(em"missing arguments for $meth", tree.srcPos) + class Errors(using Context) { /** An explanatory note to be added to error messages diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 85780105840c..41e41eb9a1a8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1356,7 +1356,7 @@ class Typer extends Namer else cpy.ValDef(param)( tpt = untpd.TypeTree( inferredParamType(param, protoFormal(i)).translateFromRepeated(toArray = false))) - desugar.makeClosure(inferredParams, fnBody, resultTpt, isContextual) + desugar.makeClosure(inferredParams, fnBody, resultTpt, isContextual, tree.span) } typed(desugared, pt) } @@ -3114,12 +3114,9 @@ class Typer extends Namer def readapt(tree: Tree, shouldTryGadtHealing: Boolean = tryGadtHealing)(using Context) = adapt(tree, pt, locked, shouldTryGadtHealing) def readaptSimplified(tree: Tree)(using Context) = readapt(simplify(tree, pt, locked)) - def missingArgs(mt: MethodType) = { - val meth = err.exprStr(methPart(tree)) - if (mt.paramNames.length == 0) report.error(MissingEmptyArgumentList(meth), tree.srcPos) - else report.error(em"missing arguments for $meth", tree.srcPos) + def missingArgs(mt: MethodType) = + ErrorReporting.missingArgs(tree, mt) tree.withType(mt.resultType) - } def adaptOverloaded(ref: TermRef) = { val altDenots = @@ -3413,11 +3410,12 @@ class Typer extends Namer // - we reference a typelevel method // - we are in a pattern // - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that) - if (arity >= 0 && - !tree.symbol.isConstructor && - !tree.symbol.isAllOf(InlineMethod) && - !ctx.mode.is(Mode.Pattern) && - !(isSyntheticApply(tree) && !functionExpected)) { + if arity >= 0 + && !tree.symbol.isConstructor + && !tree.symbol.isAllOf(InlineMethod) + && !ctx.mode.is(Mode.Pattern) + && !(isSyntheticApply(tree) && !functionExpected) + then if (!defn.isFunctionType(pt)) pt match { case SAMType(_) if !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) => @@ -3425,7 +3423,6 @@ class Typer extends Namer case _ => } simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked) - } else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol)) readaptSimplified(tpd.Apply(tree, Nil)) else if (wtp.isImplicitMethod) @@ -3832,8 +3829,20 @@ class Typer extends Namer && !tree.isInstanceOf[Inlined] && isPureExpr(tree) && !isSelfOrSuperConstrCall(tree) - then - report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos) + then tree match + case closureDef(meth) + if meth.span == meth.rhs.span.toSynthetic && !untpd.isFunction(original) => + // It's a synthesized lambda, for instance via an eta expansion: report a hard error + // There are two tests for synthetic lambdas which both have to be true. + // The first test compares spans of closure definition with the closure's right hand + // side. This is usually accurate but can fail for compiler-generated test code. + // See repl.DocTests for two failing tests. The second tests rules out closures + // if the original tree was a lambda. This does not work always either since + // sometimes we do not have the original anymore and use the transformed tree instead. + // But taken together, the two criteria are quite accurate. + missingArgs(tree, tree.tpe.widen) + case _ => + report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos) /** Types the body Scala 2 macro declaration `def f = macro ` */ private def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree = diff --git a/tests/neg-custom-args/erased/erased-pathdep-1.scala b/tests/neg-custom-args/erased/erased-pathdep-1.scala index 027a08e29582..55e8b89013b2 100644 --- a/tests/neg-custom-args/erased/erased-pathdep-1.scala +++ b/tests/neg-custom-args/erased/erased-pathdep-1.scala @@ -3,8 +3,8 @@ object Test { fun1(new Bar) - fun2(new Bar) - fun3(new Bar) + val _ = fun2(new Bar) + val _ = fun3(new Bar) def fun1[F >: Bar <: Foo](erased f: F): f.X = null.asInstanceOf[f.X] // error // error def fun2[F >: Bar <: Foo](erased f: F)(erased bar: f.B): f.B = null.asInstanceOf[f.B] // error // error // error diff --git a/tests/neg/i11761.scala b/tests/neg/i11761.scala new file mode 100644 index 000000000000..5c8488d738ba --- /dev/null +++ b/tests/neg/i11761.scala @@ -0,0 +1,8 @@ +def go(x: Int): Unit = + go // error + go // error + go // error + +def foo: Unit = + (x: Int) => go(x) // warning + diff --git a/tests/neg/i5311.check b/tests/neg/i5311.check index cc877b020d5e..bd2940afc7a9 100644 --- a/tests/neg/i5311.check +++ b/tests/neg/i5311.check @@ -1,7 +1,7 @@ --- [E007] Type Mismatch Error: tests/neg/i5311.scala:11:9 -------------------------------------------------------------- +-- [E007] Type Mismatch Error: tests/neg/i5311.scala:11:8 -------------------------------------------------------------- 11 | baz((x : s.T[Int]) => x) // error - | ^^^^^^^^^^^^^^^^^^ - | Found: s.T[Int] => s.T[Int] - | Required: m.Foo + | ^^^^^^^^^^^^^^^^^^^ + | Found: s.T[Int] => s.T[Int] + | Required: m.Foo longer explanation available when compiling with `-explain` diff --git a/tests/neg/i7359-g.check b/tests/neg/i7359-g.check index f750b024c016..43257ae2a596 100644 --- a/tests/neg/i7359-g.check +++ b/tests/neg/i7359-g.check @@ -1,7 +1,7 @@ --- [E007] Type Mismatch Error: tests/neg/i7359-g.scala:5:25 ------------------------------------------------------------ +-- [E007] Type Mismatch Error: tests/neg/i7359-g.scala:5:19 ------------------------------------------------------------ 5 |val m : SAMTrait = () => "Hello" // error - | ^^^^^^^ - | Found: () => String - | Required: SAMTrait + | ^^^^^^^^^^^^^ + | Found: () => String + | Required: SAMTrait longer explanation available when compiling with `-explain` diff --git a/tests/pos/i3873.scala b/tests/pos/i3873.scala index f86196b1df43..d5c759251c57 100644 --- a/tests/pos/i3873.scala +++ b/tests/pos/i3873.scala @@ -1,6 +1,6 @@ object Test { inline def sum2(ys: List[Int]): Unit = { - ys.foldLeft(1) + val _ = ys.foldLeft(1) } val h1 = (xs: List[Int]) => sum2(xs) } diff --git a/tests/pos/typedapply.scala b/tests/pos/typedapply.scala index 8496d528bb67..f73839eb478c 100644 --- a/tests/pos/typedapply.scala +++ b/tests/pos/typedapply.scala @@ -6,6 +6,6 @@ object typedapply { foo[Int, String](1, "abc") - foo[Int, String] _ + val x = foo[Int, String] _ }