From b161bd0712541198a5a76899a799e703c9f8bda4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Dec 2018 11:12:42 +0100 Subject: [PATCH] Better error messages for missing symbols When I changed branches, the compiler encountered a missing required symbol "DottyPredef.ImplicitConverter" because in the orevious branch that symbol had been renamed to "ImplicitConversion". The error message was not exactly helpful, however: None of the required alternatives of satisfies required predicate This commit fixes this. It gives a meaningful error messages in all cases where a required symbol is missing. --- .../dotty/tools/dotc/core/Definitions.scala | 6 +-- .../dotty/tools/dotc/core/Denotations.scala | 44 ++++++++++++++----- .../src/dotty/tools/dotc/core/Symbols.scala | 31 ++++++++----- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 8078b5da6e24..4c5de427bfb4 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -496,10 +496,10 @@ class Definitions { def Int_<= : Symbol = Int_leR.symbol lazy val LongType: TypeRef = valueTypeRef("scala.Long", BoxedLongType, java.lang.Long.TYPE, LongEnc, nme.specializedTypeNames.Long) def LongClass(implicit ctx: Context): ClassSymbol = LongType.symbol.asClass - lazy val Long_XOR_Long: Symbol = LongType.member(nme.XOR).requiredSymbol( + lazy val Long_XOR_Long: Symbol = LongType.member(nme.XOR).requiredSymbol("method", nme.XOR, LongType.denot)( x => (x is Method) && (x.info.firstParamTypes.head isRef defn.LongClass) ) - lazy val Long_LSR_Int: Symbol = LongType.member(nme.LSR).requiredSymbol( + lazy val Long_LSR_Int: Symbol = LongType.member(nme.LSR).requiredSymbol("method", nme.LSR, LongType.denot)( x => (x is Method) && (x.info.firstParamTypes.head isRef defn.IntClass) ) lazy val Long_plusR: TermRef = LongClass.requiredMethodRef(nme.PLUS, List(LongType)) @@ -729,7 +729,7 @@ class Definitions { def TupleXXLModule(implicit ctx: Context): Symbol = TupleXXLClass.companionModule def TupleXXL_apply(implicit ctx: Context): Symbol = - TupleXXLModule.info.member(nme.apply).requiredSymbol(_.info.isVarArgsMethod) + TupleXXLModule.info.member(nme.apply).requiredSymbol("method", nme.apply, TupleXXLModule)(_.info.isVarArgsMethod) // Annotation base classes lazy val AnnotationType: TypeRef = ctx.requiredClassRef("scala.annotation.Annotation") diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 15eb8c1e65a9..b5aab2564839 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -276,7 +276,14 @@ object Denotations { * if generateStubs is specified, return a stubsymbol if denotation is a missing ref. * Throw a `TypeError` if predicate fails to disambiguate symbol or no alternative matches. */ - def requiredSymbol(p: Symbol => Boolean, source: AbstractFile = null, generateStubs: Boolean = true)(implicit ctx: Context): Symbol = + def requiredSymbol(kind: String, + name: Name, + site: Denotation = NoDenotation, + args: List[Type] = Nil, + source: AbstractFile = null, + generateStubs: Boolean = true) + (p: Symbol => Boolean) + (implicit ctx: Context): Symbol = disambiguate(p) match { case m @ MissingRef(ownerd, name) => if (generateStubs) { @@ -285,18 +292,25 @@ object Denotations { } else NoSymbol case NoDenotation | _: NoQualifyingRef => - throw new TypeError(i"None of the alternatives of $this satisfies required predicate") + def argStr = if (args.isEmpty) "" else i" matching ($args%, %)" + val msg = + if (site.exists) i"$site does not have a member $kind $name$argStr" + else i"missing: $kind $name$argStr" + throw new TypeError(msg) case denot => denot.symbol } - def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(_ is Method).asTerm + def requiredMethod(pname: PreName)(implicit ctx: Context): TermSymbol = { + val name = pname.toTermName + info.member(name).requiredSymbol("method", name, this)(_ is Method).asTerm + } def requiredMethodRef(name: PreName)(implicit ctx: Context): TermRef = requiredMethod(name).termRef - def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = { - info.member(name.toTermName).requiredSymbol { x => + def requiredMethod(pname: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = { + val name = pname.toTermName + info.member(name).requiredSymbol(i"method", name, this, argTypes) { x => (x is Method) && { x.info.paramInfoss match { case paramInfos :: Nil => paramInfos.corresponds(argTypes)(_ =:= _) @@ -308,16 +322,22 @@ object Denotations { def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef = requiredMethod(name, argTypes).termRef - def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm + def requiredValue(pname: PreName)(implicit ctx: Context): TermSymbol = { + val name = pname.toTermName + info.member(name).requiredSymbol("field or getter", name, this)(_.info.isParameterless).asTerm + } def requiredValueRef(name: PreName)(implicit ctx: Context): TermRef = requiredValue(name).termRef - def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = - info.member(name.toTypeName).requiredSymbol(_.isClass).asClass + def requiredClass(pname: PreName)(implicit ctx: Context): ClassSymbol = { + val name = pname.toTypeName + info.member(name).requiredSymbol("class", name, this)(_.isClass).asClass + } - def requiredType(name: PreName)(implicit ctx: Context): TypeSymbol = - info.member(name.toTypeName).requiredSymbol(_.isType).asType + def requiredType(pname: PreName)(implicit ctx: Context): TypeSymbol = { + val name = pname.toTypeName + info.member(name).requiredSymbol("type", name, this)(_.isType).asType + } /** The alternative of this denotation that has a type matching `targetType` when seen * as a member of type `site`, `NoDenotation` if none exists. diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index a0f7ff9111f4..c4dc5cd61c45 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -359,32 +359,43 @@ trait Symbols { this: Context => // ----- Locating predefined symbols ---------------------------------------- - def requiredPackage(path: PreName): TermSymbol = - base.staticRef(path.toTermName, isPackage = true).requiredSymbol(_ is Package).asTerm + def requiredPackage(path: PreName): TermSymbol = { + val name = path.toTermName + base.staticRef(name, isPackage = true).requiredSymbol("package", name)(_ is Package).asTerm + } def requiredPackageRef(path: PreName): TermRef = requiredPackage(path).termRef - def requiredClass(path: PreName): ClassSymbol = - base.staticRef(path.toTypeName).requiredSymbol(_.isClass) match { + def requiredClass(path: PreName): ClassSymbol = { + val name = path.toTypeName + base.staticRef(name).requiredSymbol("class", name)(_.isClass) match { case cls: ClassSymbol => cls case sym => defn.AnyClass } + } def requiredClassRef(path: PreName): TypeRef = requiredClass(path).typeRef /** Get ClassSymbol if class is either defined in current compilation run * or present on classpath. * Returns NoSymbol otherwise. */ - def getClassIfDefined(path: PreName): Symbol = - base.staticRef(path.toTypeName, generateStubs = false).requiredSymbol(_.isClass, generateStubs = false) + def getClassIfDefined(path: PreName): Symbol = { + val name = path.toTypeName + base.staticRef(name, generateStubs = false) + .requiredSymbol("class", name, generateStubs = false)(_.isClass) + } - def requiredModule(path: PreName): TermSymbol = - base.staticRef(path.toTermName).requiredSymbol(_ is Module).asTerm + def requiredModule(path: PreName): TermSymbol = { + val name = path.toTermName + base.staticRef(name).requiredSymbol("object", name)(_ is Module).asTerm + } def requiredModuleRef(path: PreName): TermRef = requiredModule(path).termRef - def requiredMethod(path: PreName): TermSymbol = - base.staticRef(path.toTermName).requiredSymbol(_ is Method).asTerm + def requiredMethod(path: PreName): TermSymbol = { + val name = path.toTermName + base.staticRef(name).requiredSymbol("method", name)(_ is Method).asTerm + } def requiredMethodRef(path: PreName): TermRef = requiredMethod(path).termRef }