diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 1fdb513a2f55..e6943bca6ce3 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2447,8 +2447,8 @@ import transform.SymUtils._ class InvalidReferenceInImplicitNotFoundAnnotation(typeVar: String, owner: String)(using Context) extends ReferenceMsg(InvalidReferenceInImplicitNotFoundAnnotationID) { - def msg = em"""|Invalid reference to a type variable "${hl(typeVar)}" found in the annotation argument. - |The variable does not occur in the signature of ${hl(owner)}. + def msg = em"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. + |The variable does not occur as a parameter in the scope of ${hl(owner)}. |""".stripMargin def explain = "" } diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 61ccd2d9f530..d25e095b3325 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -6,12 +6,16 @@ import ast._ import core._ import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._ import Implicits._, Flags._, Constants.Constant +import Trees._ +import NameOps._ import util.Spans._ import util.SrcPos import config.Feature import java.util.regex.Matcher.quoteReplacement import reporting._ +import scala.util.matching.Regex + object ErrorReporting { import tpd._ @@ -131,14 +135,6 @@ object ErrorReporting { * all occurrences of `${X}` where `X` is in `paramNames` with the * corresponding shown type in `args`. */ - def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { - def translate(name: String): Option[String] = { - assert(paramNames.length == args.length) - val idx = paramNames.indexOf(name) - if (idx >= 0) Some(quoteReplacement(ex"${args(idx)}")) else None - } - """\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init)) - } def rewriteNotice: String = if Feature.migrateTo3 then "\nThis patch can be inserted automatically under -rewrite." @@ -180,9 +176,180 @@ object ErrorReporting { end selectErrorAddendum } + def substitutableTypeSymbolsInScope(sym: Symbol)(using Context): List[Symbol] = + sym.ownersIterator.takeWhile(!_.is(Flags.Package)).flatMap { ownerSym => + ownerSym.paramSymss.flatten.filter(_.isType) ++ + ownerSym.typeRef.nonClassTypeMembers.map(_.symbol) + }.toList + def dependentStr = """Term-dependent types are experimental, |they must be enabled with a `experimental.dependent` language import or setting""".stripMargin def err(using Context): Errors = new Errors } + + +class ImplicitSearchError( + arg: tpd.Tree, + pt: Type, + where: String, + paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, + ignoredInstanceNormalImport: => Option[SearchSuccess], + importSuggestionAddendum: => String +)(using ctx: Context) { + def missingArgMsg = arg.tpe match { + case ambi: AmbiguousImplicits => + (ambi.alt1, ambi.alt2) match { + case (alt @ AmbiguousImplicitMsg(msg), _) => + userDefinedAmbiguousImplicitMsg(alt, msg) + case (_, alt @ AmbiguousImplicitMsg(msg)) => + userDefinedAmbiguousImplicitMsg(alt, msg) + case _ => + defaultAmbiguousImplicitMsg(ambi) + } + case _ => + val shortMessage = userDefinedImplicitNotFoundParamMessage + .orElse(userDefinedImplicitNotFoundTypeMessage) + .getOrElse(defaultImplicitNotFoundMessage) + formatMsg(shortMessage)() ++ hiddenImplicitsAddendum + } + + private def formatMsg(shortForm: String)(headline: String = shortForm) = arg match { + case arg: Trees.SearchFailureIdent[?] => + shortForm + case _ => + arg.tpe match { + case tpe: SearchFailureType => + val original = arg match + case Inlined(call, _, _) => call + case _ => arg + + i"""$headline. + |I found: + | + | ${original.show.replace("\n", "\n ")} + | + |But ${tpe.explanation}.""" + } + } + + private def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { + def translate(name: String): Option[String] = { + val idx = paramNames.indexOf(name) + if (idx >= 0) Some(ex"${args(idx)}") else None + } + + """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { + case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")) + }) + } + + /** Extract a user defined error message from a symbol `sym` + * with an annotation matching the given class symbol `cls`. + */ + private def userDefinedMsg(sym: Symbol, cls: Symbol) = for { + ann <- sym.getAnnotation(cls) + Trees.Literal(Constant(msg: String)) <- ann.argument(0) + } yield msg + + private def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" + + private def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = { + formatMsg(s"ambiguous implicit arguments: ${ambi.explanation}${location("of")}")( + s"ambiguous implicit arguments of type ${pt.show} found${location("for")}" + ) + } + + private def defaultImplicitNotFoundMessage = { + em"no implicit argument of type $pt was found${location("for")}" + } + + /** Construct a custom error message given an ambiguous implicit + * candidate `alt` and a user defined message `raw`. + */ + private def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { + val params = alt.ref.underlying match { + case p: PolyType => p.paramNames.map(_.toString) + case _ => Nil + } + def resolveTypes(targs: List[tpd.Tree])(using Context) = + targs.map(a => Inferencing.fullyDefinedType(a.tpe, "type argument", a.span)) + + // We can extract type arguments from: + // - a function call: + // @implicitAmbiguous("msg A=${A}") + // implicit def f[A](): String = ... + // implicitly[String] // found: f[Any]() + // + // - an eta-expanded function: + // @implicitAmbiguous("msg A=${A}") + // implicit def f[A](x: Int): String = ... + // implicitly[Int => String] // found: x => f[Any](x) + + val call = tpd.closureBody(alt.tree) // the tree itself if not a closure + val (_, targs, _) = tpd.decomposeCall(call) + val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) + userDefinedErrorString(raw, params, args) + } + + /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" + * @param sym Symbol of the annotated type or of the method whose parameter was annotated + * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int + */ + private def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = { + val substitutableTypesSymbols = ErrorReporting.substitutableTypeSymbolsInScope(sym) + + userDefinedErrorString( + rawMsg, + paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), + args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) + ) + } + + /** Extracting the message from a method parameter, e.g. in + * + * trait Foo + * + * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? + */ + private def userDefinedImplicitNotFoundParamMessage = paramSymWithMethodCallTree.flatMap { (sym, applTree) => + userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => + val (fn, targs, _) = tpd.decomposeCall(applTree) + val methodOwner = fn.symbol.owner + val methodOwnerType = tpd.qualifier(fn).tpe + val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) + val methodTypeArgs = targs.map(_.tpe) + val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) + formatAnnotationMessage(rawMsg, sym.owner, substituteType) + } + } + + /** Extracting the message from a type, e.g. in + * + * @annotation.implicitNotFound("Foo is missing") + * trait Foo + * + * def foo(implicit foo: Foo): Any = ??? + */ + private def userDefinedImplicitNotFoundTypeMessage = + val classSym = pt.classSymbol + userDefinedMsg(classSym, defn.ImplicitNotFoundAnnot).map { rawMsg => + val substituteType = (_: Type).asSeenFrom(pt, classSym) + formatAnnotationMessage(rawMsg, classSym, substituteType) + } + + private def hiddenImplicitsAddendum: String = + def hiddenImplicitNote(s: SearchSuccess) = + em"\n\nNote: given instance ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + + val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) + + normalImports.getOrElse(importSuggestionAddendum) + end hiddenImplicitsAddendum + + private object AmbiguousImplicitMsg { + def unapply(search: SearchSuccess): Option[String] = + userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) + } +} diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 6e7185d73bfb..c97c41098182 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -82,6 +82,7 @@ object Implicits: def strictEquality(using Context): Boolean = ctx.mode.is(Mode.StrictEquality) || Feature.enabled(nme.strictEquality) + /** A common base class of contextual implicits and of-type implicits which * represents a set of references to implicit definitions. */ @@ -832,126 +833,40 @@ trait Implicits: arg } - def missingArgMsg(arg: Tree, pt: Type, where: String)(using Context): String = { - - def msg(shortForm: String)(headline: String = shortForm) = arg match { - case arg: Trees.SearchFailureIdent[?] => - shortForm - case _ => - arg.tpe match { - case tpe: SearchFailureType => - val original = arg match - case Inlined(call, _, _) => call - case _ => arg - - i"""$headline. - |I found: - | - | ${original.show.replace("\n", "\n ")} - | - |But ${tpe.explanation}.""" - } - } - - def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" - - /** Extract a user defined error message from a symbol `sym` - * with an annotation matching the given class symbol `cls`. - */ - def userDefinedMsg(sym: Symbol, cls: Symbol) = for { - ann <- sym.getAnnotation(cls) - Trees.Literal(Constant(msg: String)) <- ann.argument(0) - } - yield msg - - - arg.tpe match { - case ambi: AmbiguousImplicits => - object AmbiguousImplicitMsg { - def unapply(search: SearchSuccess): Option[String] = - userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) - } - - /** Construct a custom error message given an ambiguous implicit - * candidate `alt` and a user defined message `raw`. - */ - def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { - val params = alt.ref.underlying match { - case p: PolyType => p.paramNames.map(_.toString) - case _ => Nil + /** @param arg Tree representing a failed result of implicit search + * @param pt Type for which an implicit value was searched + * @param where Description of where the search was performed. Might be empty + * @param paramSymWithMethodCallTree Symbol of the parameter for which the implicit was searched and tree of the method call that triggered the implicit search + */ + def missingArgMsg( + arg: Tree, + pt: Type, + where: String, + paramSymWithMethodCallTree: Option[(Symbol, Tree)] = None + )(using Context): String = { + def findHiddenImplicitsCtx(c: Context): Context = + if c == NoContext then c + else c.freshOver(findHiddenImplicitsCtx(c.outer)).addMode(Mode.FindHiddenImplicits) + + def ignoredInstanceNormalImport = arg.tpe match + case fail: SearchFailureType => + if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then + inferImplicit(fail.expectedType, fail.argument, arg.span) match { + case s: SearchSuccess => Some(s) + case f: SearchFailure => + f.reason match { + case ambi: AmbiguousImplicits => Some(ambi.alt1) + case r => None + } } - def resolveTypes(targs: List[Tree])(using Context) = - targs.map(a => fullyDefinedType(a.tpe, "type argument", a.span)) - - // We can extract type arguments from: - // - a function call: - // @implicitAmbiguous("msg A=${A}") - // implicit def f[A](): String = ... - // implicitly[String] // found: f[Any]() - // - // - an eta-expanded function: - // @implicitAmbiguous("msg A=${A}") - // implicit def f[A](x: Int): String = ... - // implicitly[Int => String] // found: x => f[Any](x) - - val call = closureBody(alt.tree) // the tree itself if not a closure - val (_, targs, _) = decomposeCall(call) - val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) - err.userDefinedErrorString(raw, params, args) - } - - (ambi.alt1, ambi.alt2) match { - case (alt @ AmbiguousImplicitMsg(msg), _) => - userDefinedAmbiguousImplicitMsg(alt, msg) - case (_, alt @ AmbiguousImplicitMsg(msg)) => - userDefinedAmbiguousImplicitMsg(alt, msg) - case _ => - msg(s"ambiguous implicit arguments: ${ambi.explanation}${location("of")}")( - s"ambiguous implicit arguments of type ${pt.show} found${location("for")}") - } + else + // It's unsafe to search for parts of the expected type if they are not fully defined, + // since these come with nested contexts that are lost at this point. See #7249 for an + // example where searching for a nested type causes an infinite loop. + None - case _ => - val userDefined = userDefinedMsg(pt.typeSymbol, defn.ImplicitNotFoundAnnot).map(raw => - err.userDefinedErrorString( - raw, - pt.typeSymbol.typeParams.map(_.name.unexpandedName.toString), - pt.widenExpr.dropDependentRefinement.argInfos)) - - def hiddenImplicitsAddendum: String = - - def hiddenImplicitNote(s: SearchSuccess) = - em"\n\nNote: given instance ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - - def findHiddenImplicitsCtx(c: Context): Context = - if c == NoContext then c - else c.freshOver(findHiddenImplicitsCtx(c.outer)).addMode(Mode.FindHiddenImplicits) - - val normalImports = arg.tpe match - case fail: SearchFailureType => - if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - inferImplicit(fail.expectedType, fail.argument, arg.span)( - using findHiddenImplicitsCtx(ctx)) match { - case s: SearchSuccess => hiddenImplicitNote(s) - case f: SearchFailure => - f.reason match { - case ambi: AmbiguousImplicits => hiddenImplicitNote(ambi.alt1) - case r => "" - } - } - else - // It's unsafe to search for parts of the expected type if they are not fully defined, - // since these come with nested contexts that are lost at this point. See #7249 for an - // example where searching for a nested type causes an infinite loop. - "" - - def suggestedImports = importSuggestionAddendum(pt) - if normalImports.isEmpty then suggestedImports else normalImports - end hiddenImplicitsAddendum - - msg(userDefined.getOrElse( - em"no implicit argument of type $pt was found${location("for")}"))() ++ - hiddenImplicitsAddendum - } + val error = new ImplicitSearchError(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, importSuggestionAddendum(pt)) + error.missingArgMsg } /** A string indicating the formal parameter corresponding to a missing argument */ diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 60e96102c287..89b2ddff0c49 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -23,7 +23,7 @@ import scala.util.matching.Regex._ import Constants.Constant object RefChecks { - import tpd.{Tree, MemberDef, Literal, Template, DefDef} + import tpd.{Tree, MemberDef, NamedArg, Literal, Template, DefDef} val name: String = "refchecks" @@ -957,18 +957,11 @@ object RefChecks { * (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.) */ private object checkImplicitNotFoundAnnotation: - /** Warns if the class or trait has an @implicitNotFound annotation * with invalid type variable references. */ def template(sd: SymDenotation)(using Context): Unit = - for - annotation <- sd.getAnnotation(defn.ImplicitNotFoundAnnot) - l@Literal(c: Constant) <- annotation.argument(0) - do forEachTypeVariableReferenceIn(c.stringValue) { case (ref, start) => - if !sd.typeParams.exists(_.denot.name.show == ref) then - reportInvalidReferences(l, ref, start, sd) - } + checkReferences(sd) /** Warns if the def has parameters with an `@implicitNotFound` annotation * with invalid type variable references. @@ -977,26 +970,42 @@ object RefChecks { for paramSymss <- sd.paramSymss param <- paramSymss - do - for - annotation <- param.getAnnotation(defn.ImplicitNotFoundAnnot) - l@Literal(c: Constant) <- annotation.argument(0) - do forEachTypeVariableReferenceIn(c.stringValue) { case (ref, start) => - if !sd.paramSymss.flatten.exists(_.name.show == ref) then - reportInvalidReferences(l, ref, start, sd) - } + if param.isTerm + do checkReferences(param.denot) + + private object PositionedStringLiteralArgument: + def unapply(tree: Tree): Option[(String, Span)] = tree match { + case l@Literal(Constant(s: String)) => Some((s, l.span)) + case NamedArg(_, l@Literal(Constant(s: String))) => Some((s, l.span)) + case _ => None + } + + private def checkReferences(sd: SymDenotation)(using Context): Unit = + lazy val substitutableTypesNames = + ErrorReporting.substitutableTypeSymbolsInScope(sd.symbol).map(_.denot.name.show) + for + annotation <- sd.getAnnotation(defn.ImplicitNotFoundAnnot) + PositionedStringLiteralArgument(msg, span) <- annotation.argument(0) + do forEachTypeVariableReferenceIn(msg) { case (ref, start) => + if !substitutableTypesNames.contains(ref) then + reportInvalidReference(span, ref, start, sd) + } - /** Reports an invalid reference to a type variable `typeRef` that was found in `l` */ - private def reportInvalidReferences( - l: Literal, + /** Reports an invalid reference to a type variable `typeRef` that was found in `span` */ + private def reportInvalidReference( + span: Span, typeRef: String, - offsetInLiteral: Int, + variableOffsetinArgumentLiteral: Int, sd: SymDenotation )(using Context) = - val msg = InvalidReferenceInImplicitNotFoundAnnotation( - typeRef, if (sd.isConstructor) "the constructor" else sd.name.show) - val span = l.span.shift(offsetInLiteral + 1) // +1 because of 0-based index - val pos = ctx.source.atSpan(span.startPos) + val typeRefName = s"`$typeRef`" + val ownerName = + if sd.isType then s"type `${sd.name.show}`" + else if sd.owner.isConstructor then s"the constructor of `${sd.owner.owner.name.show}`" + else s"method `${sd.owner.name.show}`" + val msg = InvalidReferenceInImplicitNotFoundAnnotation(typeRefName, ownerName) + val startPos = span.shift(variableOffsetinArgumentLiteral + 1).startPos // +1 because of 0-based index + val pos = ctx.source.atSpan(startPos) report.warning(msg, pos) /** Calls the supplied function for each quoted reference to a type variable in
s
. @@ -1013,10 +1022,15 @@ object RefChecks { * @param f A function to apply to every pair of (\, \). */ private def forEachTypeVariableReferenceIn(s: String)(f: (String, Int) => Unit) = - // matches quoted references such as "${(A)}", "${(Abc)}", etc. - val reference = """(?<=\$\{)[a-zA-Z]+(?=\})""".r - val matches = reference.findAllIn(s) - for m <- matches do f(m, matches.start) + // matches quoted references such as "${A}", "${ Abc }", etc. + val referencePattern = """\$\{\s*([^}\s]+)\s*\}""".r + val matches = referencePattern.findAllIn(s) + for reference <- matches do + val referenceOffset = matches.start + val prefixlessReference = reference.replaceFirst("""\$\{\s*""", "") + val variableOffset = referenceOffset + reference.length - prefixlessReference.length + val variableName = prefixlessReference.replaceFirst("""\s*\}""", "") + f(variableName, variableOffset) end checkImplicitNotFoundAnnotation diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a8ae701e3a27..fd1bdaab2a9c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2003,6 +2003,13 @@ class Typer extends Namer checkThisConstrCall(rhs1) } + if sym.is(Method) && sym.owner.denot.isRefinementClass then + for annot <- sym.paramSymss.flatten.filter(_.isTerm).flatMap(_.getAnnotation(defn.ImplicitNotFoundAnnot)) do + report.warning( + i"The annotation ${defn.ImplicitNotFoundAnnot} is not allowed on parameters of methods defined inside a refinement and it will have no effect", + annot.tree.sourcePos + ) + val ddef2 = assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) checkSignatureRepeatedParam(sym) @@ -3108,12 +3115,20 @@ class Typer extends Namer val propFail = propagatedFailure(args) def issueErrors(): Tree = { + def paramSymWithMethodTree(paramName: TermName) = + if tree.symbol.exists then + val paramSyms = tree.symbol.paramSymss.flatten.map(sym => sym.name -> sym).toMap + Some((paramSyms(paramName), tree)) + else + None + wtp.paramNames.lazyZip(wtp.paramInfos).lazyZip(args).foreach { (paramName, formal, arg) => arg.tpe match { case failure: SearchFailureType => report.error( - missingArgMsg(arg, formal, implicitParamString(paramName, methodStr, tree)), - tree.srcPos.endPos) + missingArgMsg(arg, formal, implicitParamString(paramName, methodStr, tree), paramSymWithMethodTree(paramName)), + tree.srcPos.endPos + ) case _ => } } diff --git a/tests/neg-custom-args/fatal-warnings/i4008.check b/tests/neg-custom-args/fatal-warnings/i4008.check index c04a7949e9fd..a6e206f623d7 100644 --- a/tests/neg-custom-args/fatal-warnings/i4008.check +++ b/tests/neg-custom-args/fatal-warnings/i4008.check @@ -1,40 +1,40 @@ -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:5:56 --------------------------------------- 5 |@annotation.implicitNotFound("An implicit ShouldWarn1[${B}] is not in scope") // error | ^ - | Invalid reference to a type variable "B" found in the annotation argument. - | The variable does not occur in the signature of ShouldWarn1. + | Invalid reference to a type variable `B` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `ShouldWarn1`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:9:56 --------------------------------------- 9 |@annotation.implicitNotFound("An implicit ShouldWarn2[${A}] is not in scope") // error | ^ - | Invalid reference to a type variable "A" found in the annotation argument. - | The variable does not occur in the signature of ShouldWarn2. + | Invalid reference to a type variable `A` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `ShouldWarn2`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:13:56 -------------------------------------- 13 |@annotation.implicitNotFound("An implicit ShouldWarn3[${A},${B}] is not in scope") // error | ^ - | Invalid reference to a type variable "A" found in the annotation argument. - | The variable does not occur in the signature of ShouldWarn3. + | Invalid reference to a type variable `A` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `ShouldWarn3`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:17:56 -------------------------------------- 17 |@annotation.implicitNotFound("An implicit ShouldWarn4[${A},${B}] is not in scope") // error // error | ^ - | Invalid reference to a type variable "A" found in the annotation argument. - | The variable does not occur in the signature of ShouldWarn4. + | Invalid reference to a type variable `A` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `ShouldWarn4`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:17:61 -------------------------------------- 17 |@annotation.implicitNotFound("An implicit ShouldWarn4[${A},${B}] is not in scope") // error // error | ^ - | Invalid reference to a type variable "B" found in the annotation argument. - | The variable does not occur in the signature of ShouldWarn4. + | Invalid reference to a type variable `B` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `ShouldWarn4`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:21:61 -------------------------------------- 21 |@annotation.implicitNotFound("An implicit ShouldWarn5[${C},${Abc}] is not in scope") // error | ^ - | Invalid reference to a type variable "Abc" found in the annotation argument. - | The variable does not occur in the signature of ShouldWarn5. + | Invalid reference to a type variable `Abc` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `ShouldWarn5`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:44:54 -------------------------------------- 44 |class C[A](using @annotation.implicitNotFound("No C[${B}] found") c: Class[A]) // error | ^ - | Invalid reference to a type variable "B" found in the annotation argument. - | The variable does not occur in the signature of the constructor. + | Invalid reference to a type variable `B` found in the annotation argument. + | The variable does not occur as a parameter in the scope of the constructor of `C`. -- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:46:62 -------------------------------------- 46 |def someMethod1[A](using @annotation.implicitNotFound("No C[${B}] found") sc: C[A]) = 0 // error | ^ - | Invalid reference to a type variable "B" found in the annotation argument. - | The variable does not occur in the signature of someMethod1. + | Invalid reference to a type variable `B` found in the annotation argument. + | The variable does not occur as a parameter in the scope of method `someMethod1`. diff --git a/tests/neg-custom-args/fatal-warnings/i4986b.check b/tests/neg-custom-args/fatal-warnings/i4986b.check new file mode 100644 index 000000000000..2e74c29b077c --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i4986b.check @@ -0,0 +1,40 @@ +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:3:65 -------------------------------------- +3 |@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") // error // error + | ^ + | Invalid reference to a type variable `Too` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `Meh`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:3:94 -------------------------------------- +3 |@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") // error // error + | ^ + | Invalid reference to a type variable `Elem` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `Meh`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:6:71 -------------------------------------- +6 |@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.") // error + | ^ + | Invalid reference to a type variable `Elem` found in the annotation argument. + | The variable does not occur as a parameter in the scope of type `Meh2`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:9:46 -------------------------------------- +9 |class C[T](implicit @implicitNotFound("No C[${t}] available") t: T) // error + | ^ + | Invalid reference to a type variable `t` found in the annotation argument. + | The variable does not occur as a parameter in the scope of the constructor of `C`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:12:54 ------------------------------------- +12 | def m[Aaa](implicit @implicitNotFound("I see no C[${Uuh}]") theC: C[Aaa]) = ??? // error + | ^ + | Invalid reference to a type variable `Uuh` found in the annotation argument. + | The variable does not occur as a parameter in the scope of method `m`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:18:73 ------------------------------------- +18 | def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error + | ^ + | Invalid reference to a type variable `XX` found in the annotation argument. + | The variable does not occur as a parameter in the scope of method `m`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:18:79 ------------------------------------- +18 | def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error + | ^ + | Invalid reference to a type variable `ZZ` found in the annotation argument. + | The variable does not occur as a parameter in the scope of method `m`. +-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:18:86 ------------------------------------- +18 | def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error + | ^ + | Invalid reference to a type variable `Nix` found in the annotation argument. + | The variable does not occur as a parameter in the scope of method `m`. diff --git a/tests/neg-custom-args/fatal-warnings/i4986b.scala b/tests/neg-custom-args/fatal-warnings/i4986b.scala new file mode 100644 index 000000000000..e4ee1e64e8fe --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i4986b.scala @@ -0,0 +1,20 @@ +import scala.annotation.implicitNotFound + +@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") // error // error +trait Meh[-From, +To] + +@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.") // error +trait Meh2[-From, +To] + +class C[T](implicit @implicitNotFound("No C[${t}] available") t: T) // error + +trait T { + def m[Aaa](implicit @implicitNotFound("I see no C[${Uuh}]") theC: C[Aaa]) = ??? // error + def n[Aaa](implicit @implicitNotFound("I see no C[${Aaa}]") theC: C[Aaa]) = ??? +} + +trait U[X, Y[_], Z[_, ZZ]] { + class I[R] { + def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error + } +} diff --git a/tests/neg-custom-args/fatal-warnings/i4986d.scala b/tests/neg-custom-args/fatal-warnings/i4986d.scala new file mode 100644 index 000000000000..74a3c01aaa71 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i4986d.scala @@ -0,0 +1,22 @@ +trait Foo[A] + +type Fooable[A] = { + def foo(implicit @annotation.implicitNotFound("There's no Foo[${A}]") ev: Foo[A]): Any // error + + type InnerFooable = { + def foo(implicit @annotation.implicitNotFound("There's no Foo[${A}]") ev: Foo[A]): Any // error + } +} + +object Fooable { + (null: Fooable[Long]).foo // error + (null: Fooable[Long]#InnerFooable).foo // error +} + + +@annotation.implicitNotFound("There's no Bar[${A}]") +trait Bar[A] + +type Barable[A] = { + def bar(implicit ev: Bar[A]): Any // ok +} diff --git a/tests/neg/i4986a.check b/tests/neg/i4986a.check new file mode 100644 index 000000000000..5375d0fd002b --- /dev/null +++ b/tests/neg/i4986a.check @@ -0,0 +1,9 @@ +-- Error: tests/neg/i4986a.scala:6:57 ---------------------------------------------------------------------------------- +6 | def foo(l: Lst[Int]) = l.map[Int, List[String]](x => 1) // error + | ^ + |Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[Int].. + |I found: + | + | collection.BuildFrom.buildFromIterableOps[Nothing, Nothing, Nothing] + | + |But method buildFromIterableOps in trait BuildFromLowPriority2 does not match type collection.BuildFrom[List[Int], Int, List[String]]. diff --git a/tests/neg/i4986a.scala b/tests/neg/i4986a.scala new file mode 100644 index 000000000000..dcf461a4b1e7 --- /dev/null +++ b/tests/neg/i4986a.scala @@ -0,0 +1,7 @@ +class Lst[+A] { + def map[B, That](f: A => B)(implicit bf: collection.BuildFrom[List[A], B, That]): That = ??? +} + +object Test { + def foo(l: Lst[Int]) = l.map[Int, List[String]](x => 1) // error +} diff --git a/tests/neg/i4986c.check b/tests/neg/i4986c.check new file mode 100644 index 000000000000..b93a5520b021 --- /dev/null +++ b/tests/neg/i4986c.check @@ -0,0 +1,64 @@ +-- Error: tests/neg/i4986c.scala:38:8 ---------------------------------------------------------------------------------- +38 | test.f // error + | ^ + | Missing X$Y for Test[Char] +-- Error: tests/neg/i4986c.scala:39:13 --------------------------------------------------------------------------------- +39 | test.g[Int] // error + | ^ + | Missing Outer[Int] with OuterMember = pkg.Outer[Int]#OuterMember +-- Error: tests/neg/i4986c.scala:40:13 --------------------------------------------------------------------------------- +40 | test.h[X$Y] // error + | ^ + | Missing Outer[pkg.X$Y] with OuterMember = pkg.Outer[pkg.X$Y]#OuterMember +-- Error: tests/neg/i4986c.scala:41:24 --------------------------------------------------------------------------------- +41 | test.i[Option[String]] // error + | ^ + | Missing implicit outer param of type Outer[Option[String]] for Test[Char] +-- Error: tests/neg/i4986c.scala:42:43 --------------------------------------------------------------------------------- +42 | test.j[(Long, Long), Int | String, Array] // error + | ^ + |Missing Inner[Int | String, Array] with InnerMember = pkg.Outer[(Long, Long)]#Inner[Int | String, Array]#InnerMember from Outer[(Long, Long)] with OuterMember = pkg.Outer[(Long, Long)]#OuterMember +-- Error: tests/neg/i4986c.scala:43:53 --------------------------------------------------------------------------------- +43 | test.k[Either[String, Any], Seq[Seq[Char]], Vector] // error + | ^ + | Missing implicit inner param of type Outer[Either[String, Any]]#Inner[Seq[Seq[Char]], Vector] for Test[Char] +-- Error: tests/neg/i4986c.scala:45:87 --------------------------------------------------------------------------------- +45 | implicitly[Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal }] // error + | ^ + |Missing Outer[Option[String] | List[Iterable[Char]]] with OuterMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{MyType = BigDecimal}#OuterMember +-- Error: tests/neg/i4986c.scala:46:106 -------------------------------------------------------------------------------- +46 | implicitly[(Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal })#Inner[Byte, Seq]] // error + | ^ + |Missing Inner[Byte, Seq] with InnerMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{MyType = BigDecimal}#Inner[Byte, Seq]#InnerMember from Outer[Option[String] | List[Iterable[Char]]] with OuterMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{MyType = BigDecimal}#OuterMember +-- Error: tests/neg/i4986c.scala:47:33 --------------------------------------------------------------------------------- +47 | implicitly[Outer[Int] @myAnnot] // error + | ^ + | Missing Outer[Int] with OuterMember = pkg.Outer[Int] @myAnnot#OuterMember +-- Error: tests/neg/i4986c.scala:52:52 --------------------------------------------------------------------------------- +52 | implicitly[Outer[Int] { type OuterMember = Long }] // error + | ^ + | Missing Outer[Int] with OuterMember = Long +-- Error: tests/neg/i4986c.scala:53:24 --------------------------------------------------------------------------------- +53 | implicitly[outer.type] // error + | ^ + | Missing Outer[Int] with OuterMember = pkg.Test.outer.OuterMember +-- Error: tests/neg/i4986c.scala:54:104 -------------------------------------------------------------------------------- +54 | implicitly[(Outer[Int] { type OuterMember = Long })#Inner[Long, Iterator] { type InnerMember = Byte }] // error + | ^ + | Missing Inner[Long, Iterator] with InnerMember = Byte from Outer[Int] with OuterMember = Long +-- Error: tests/neg/i4986c.scala:55:69 --------------------------------------------------------------------------------- +55 | implicitly[outer.Inner[Long, Iterator] { type InnerMember = Byte }] // error + | ^ + |Missing Inner[Long, Iterator] with InnerMember = Byte from Outer[Int] with OuterMember = pkg.Test.outer.OuterMember +-- Error: tests/neg/i4986c.scala:56:24 --------------------------------------------------------------------------------- +56 | implicitly[inner.type] // error + | ^ + |Missing Inner[Long, Iterator] with InnerMember = pkg.Test.inner.InnerMember from Outer[Int] with OuterMember = pkg.Test.outer.OuterMember +-- Error: tests/neg/i4986c.scala:58:33 --------------------------------------------------------------------------------- +58 | implicitly[U[Int, Option, Map]] // error + | ^ + | There's no U[Int, Option, Map] +-- Error: tests/neg/i4986c.scala:62:19 --------------------------------------------------------------------------------- +62 | i.m[Option[Long]] // error + | ^ + | String; List; [A, _$6] =>> List[Option[?]]; Int; Option[Long]; diff --git a/tests/neg/i4986c.scala b/tests/neg/i4986c.scala new file mode 100644 index 000000000000..2176c6019c0a --- /dev/null +++ b/tests/neg/i4986c.scala @@ -0,0 +1,63 @@ +package pkg + +import annotation._ + +class myAnnot extends StaticAnnotation + +@implicitNotFound("Missing Outer[${ A }] with OuterMember = ${OuterMember}") +class Outer[A] { + type OuterMember + + @implicitNotFound("Missing Inner[${B}, ${C}] with InnerMember = ${InnerMember} from Outer[${ A }] with OuterMember = ${OuterMember}") + class Inner[B, C[_]] { + type InnerMember + } +} + +trait X$Y + +@implicitNotFound("There's no U[${X}, ${Y}, ${Z}]") +trait U[X, Y[_], Z[_, ZZ]] { + class I[R] { + def m[S](implicit @implicitNotFound("${X}; ${Y}; ${ Z }; ${R}; ${S}; ${XX}") i: Int) = ??? + } +} + +class Test[A] { + def f(implicit @implicitNotFound("Missing X$Y for Test[${A}]") xy: X$Y) = ??? + def g[B: Outer] = ??? + def h[B](implicit outer: Outer[B]) = ??? + def i[B](implicit @implicitNotFound("Missing implicit outer param of type Outer[${B}] for Test[${A}]") outer: Outer[B]) = ??? + def j[B, C, D[_]](implicit inner: Outer[B]#Inner[C, D]) = ??? + def k[B, C, D[_]](implicit @implicitNotFound("Missing implicit inner param of type Outer[${B}]#Inner[${C}, ${D}] for Test[${A}]") inner: Outer[B]#Inner[C, D]) = ??? +} + +object Test { + val test = new Test[Char] + + test.f // error + test.g[Int] // error + test.h[X$Y] // error + test.i[Option[String]] // error + test.j[(Long, Long), Int | String, Array] // error + test.k[Either[String, Any], Seq[Seq[Char]], Vector] // error + + implicitly[Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal }] // error + implicitly[(Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal })#Inner[Byte, Seq]] // error + implicitly[Outer[Int] @myAnnot] // error + + val outer = new Outer[Int] { type OuterMember = Long } + val inner = new outer.Inner[Long, Iterator] { type InnerMember = Byte } + + implicitly[Outer[Int] { type OuterMember = Long }] // error + implicitly[outer.type] // error + implicitly[(Outer[Int] { type OuterMember = Long })#Inner[Long, Iterator] { type InnerMember = Byte }] // error + implicitly[outer.Inner[Long, Iterator] { type InnerMember = Byte }] // error + implicitly[inner.type] // error + + implicitly[U[Int, Option, Map]] // error + + val u = new U[String, List, [A, _] =>> List[Option[_]]] { } + val i = new u.I[Int] + i.m[Option[Long]] // error +} diff --git a/tests/untried/neg/t2462a.check b/tests/untried/neg/t2462a.check deleted file mode 100644 index 86d74b86d406..000000000000 --- a/tests/untried/neg/t2462a.check +++ /dev/null @@ -1,4 +0,0 @@ -t2462a.scala:2: error: Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[Int]. - List(1,2,3).map[Int, List[String]](x => 1) - ^ -one error found diff --git a/tests/untried/neg/t2462a.scala b/tests/untried/neg/t2462a.scala deleted file mode 100644 index 5e49314d53b0..000000000000 --- a/tests/untried/neg/t2462a.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test { - List(1,2,3).map[Int, List[String]](x => 1) -} diff --git a/tests/untried/neg/t2462b.check b/tests/untried/neg/t2462b.check deleted file mode 100644 index b3b8007a93a2..000000000000 --- a/tests/untried/neg/t2462b.check +++ /dev/null @@ -1,11 +0,0 @@ -t2462b.scala:6: warning: Invalid implicitNotFound message for trait Meh in package test: -The type parameters Too, Elem referenced in the message of the @implicitNotFound annotation are not defined by trait Meh. -trait Meh[-From, +To] - ^ -t2462b.scala:9: warning: Invalid implicitNotFound message for trait Meh2 in package test: -The type parameter Elem referenced in the message of the @implicitNotFound annotation is not defined by trait Meh2. -trait Meh2[-From, +To] - ^ -error: No warnings can be incurred under -Xfatal-warnings. -two warnings found -one error found diff --git a/tests/untried/neg/t2462b.flags b/tests/untried/neg/t2462b.flags deleted file mode 100644 index 85d8eb2ba295..000000000000 --- a/tests/untried/neg/t2462b.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings diff --git a/tests/untried/neg/t2462b.scala b/tests/untried/neg/t2462b.scala deleted file mode 100644 index 576db4bd3f33..000000000000 --- a/tests/untried/neg/t2462b.scala +++ /dev/null @@ -1,9 +0,0 @@ -package test - -import scala.annotation.implicitNotFound - -@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") -trait Meh[-From, +To] - -@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.") -trait Meh2[-From, +To] diff --git a/tests/untried/neg/t2462c.check b/tests/untried/neg/t2462c.check deleted file mode 100644 index edeead55d60c..000000000000 --- a/tests/untried/neg/t2462c.check +++ /dev/null @@ -1,7 +0,0 @@ -t2462c.scala:18: error: No C of X$Y - f[X$Y] - ^ -t2462c.scala:24: error: No C of Foo[Int] - f[Foo[Int]] - ^ -two errors found diff --git a/tests/untried/neg/t2462c.flags b/tests/untried/neg/t2462c.flags deleted file mode 100644 index 85d8eb2ba295..000000000000 --- a/tests/untried/neg/t2462c.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings diff --git a/tests/untried/neg/t2462c.scala b/tests/untried/neg/t2462c.scala deleted file mode 100644 index acf04afba954..000000000000 --- a/tests/untried/neg/t2462c.scala +++ /dev/null @@ -1,25 +0,0 @@ - -import annotation._ - -@implicitNotFound("No C of ${ A }") -class C[A] - -trait X$Y -/* using the $$ separator for expanded names is unwise -trait X$$Y -trait X$$$Y -trait X$$$$Y - */ - -trait Foo[A] - -class Test { - def f[A: C] = ??? - f[X$Y] -/* using the $$ separator for expanded names is unwise - f[X$$Y] - f[X$$$Y] - f[X$$$$Y] - */ - f[Foo[Int]] -}