From d614ae6e76249ab746a3e78af6e216301ba9bdb4 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 19 Aug 2012 18:25:35 +0200 Subject: SI-6258 Reject partial funs with undefined param types This regressed with virtpatmat. With -Xoldpatmat, pattern matching anonymous functions with an expected type of PartialFunction[A, B] are translated to a Function tree, and typed by typedFunction, which issues an error of the parameter types are not fully defined. This commit adds the same check to MatchFunTyper. It doesn't plug the hole in RefChecks#validateVariance (which is reminiscent of SI-3577.) Seems to me that in general one should handle: a) both BoundedWildcardType and WildcardType when in a place that can be called during inference, or b) neither otherwise --- test/files/neg/t6258.check | 16 ++++++++++++++++ test/files/neg/t6258.scala | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 test/files/neg/t6258.check create mode 100644 test/files/neg/t6258.scala (limited to 'test/files/neg') diff --git a/test/files/neg/t6258.check b/test/files/neg/t6258.check new file mode 100644 index 0000000000..73363d8280 --- /dev/null +++ b/test/files/neg/t6258.check @@ -0,0 +1,16 @@ +t6258.scala:2: error: missing parameter type for expanded function +The argument types of an anonymous function must be fully known. (SLS 8.5) +Expected type was: PartialFunction[?, Int] + val f : PartialFunction[_, Int] = { case a : Int => a } // undefined param + ^ +t6258.scala:5: error: missing parameter type for expanded function +The argument types of an anonymous function must be fully known. (SLS 8.5) +Expected type was: PartialFunction[?,Int] + foo { case a : Int => a } // undefined param + ^ +t6258.scala:22: error: missing parameter type for expanded function +The argument types of an anonymous function must be fully known. (SLS 8.5) +Expected type was: PartialFunction[?,Any] + bar[M[Any]] (foo { // undefined param + ^ +three errors found diff --git a/test/files/neg/t6258.scala b/test/files/neg/t6258.scala new file mode 100644 index 0000000000..5046a4750a --- /dev/null +++ b/test/files/neg/t6258.scala @@ -0,0 +1,25 @@ +object Test { + val f : PartialFunction[_, Int] = { case a : Int => a } // undefined param + + def foo[A](pf: PartialFunction[A, Int]) {}; + foo { case a : Int => a } // undefined param + + val g : PartialFunction[Int, _] = { case a : Int => a } // okay +} + + +// Another variation, seen in the wild with Specs2. +class X { + trait Matcher[-T] + + def bar[T](m: Matcher[T]) = null + def bar[T](i: Int) = null + + def foo[T](p: PartialFunction[T, Any]): Matcher[T] = null + + case class M[X](a: X) + + bar[M[Any]] (foo { // undefined param + case M(_) => null + }) +} -- cgit v1.2.3 From 816cecf9a95989dfc570f2acad87d4156b09e2ff Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 28 Aug 2012 13:37:02 -0700 Subject: More fix for invalid companions. Eliminated InvalidCompanions exception entirely. Anyone's guess why we unholstered this exception every time someone calls "isCodefinedWith" rather than when symbols are created. I moved the check into Namers, where it can be done once and with sufficient finesse not to crash so much. With this patch in place, "playbench" can be built with java7. --- .../scala/tools/nsc/typechecker/Infer.scala | 11 +++-- .../scala/tools/nsc/typechecker/Namers.scala | 49 ++++++++++++++++------ src/reflect/scala/reflect/internal/Symbols.scala | 37 +++++----------- test/files/neg/t5031.check | 6 +-- test/files/neg/t5031b.check | 5 +++ test/files/neg/t5031b/a.scala | 3 ++ test/files/neg/t5031b/b.scala | 3 ++ 7 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 test/files/neg/t5031b.check create mode 100644 test/files/neg/t5031b/a.scala create mode 100644 test/files/neg/t5031b/b.scala (limited to 'test/files/neg') diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 1b3602fca2..803fb2857e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -923,10 +923,13 @@ trait Infer { /** Is sym1 (or its companion class in case it is a module) a subclass of * sym2 (or its companion class in case it is a module)? */ - def isProperSubClassOrObject(sym1: Symbol, sym2: Symbol): Boolean = - sym1 != sym2 && sym1 != NoSymbol && (sym1 isSubClass sym2) || - sym1.isModuleClass && isProperSubClassOrObject(sym1.linkedClassOfClass, sym2) || - sym2.isModuleClass && isProperSubClassOrObject(sym1, sym2.linkedClassOfClass) + def isProperSubClassOrObject(sym1: Symbol, sym2: Symbol): Boolean = ( + (sym1 != sym2) && (sym1 != NoSymbol) && ( + (sym1 isSubClass sym2) + || (sym1.isModuleClass && isProperSubClassOrObject(sym1.linkedClassOfClass, sym2)) + || (sym2.isModuleClass && isProperSubClassOrObject(sym1, sym2.linkedClassOfClass)) + ) + ) /** is symbol `sym1` defined in a proper subclass of symbol `sym2`? */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index adced9d8c9..62f01b8afa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -359,10 +359,39 @@ trait Namers extends MethodSynthesis { } } + /** Given a ClassDef or ModuleDef, verifies there isn't a companion which + * has been defined in a separate file. + */ + private def validateCompanionDefs(tree: ImplDef) { + val sym = tree.symbol + if (sym eq NoSymbol) return + + val ctx = if (context.owner.isPackageObjectClass) context.outer else context + val module = if (sym.isModule) sym else ctx.scope lookup tree.name.toTermName + val clazz = if (sym.isClass) sym else ctx.scope lookup tree.name.toTypeName + val fails = ( + module.isModule + && clazz.isClass + && !module.isSynthetic + && !clazz.isSynthetic + && (clazz.sourceFile ne null) + && (module.sourceFile ne null) + && !(module isCoDefinedWith clazz) + ) + if (fails) { + context.unit.error(tree.pos, ( + s"Companions '$clazz' and '$module' must be defined in same file:\n" + + s" Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}") + ) + } + } + def enterModuleDef(tree: ModuleDef) = { val sym = enterModuleSymbol(tree) sym.moduleClass setInfo namerOf(sym).moduleClassTypeCompleter(tree) sym setInfo completerOf(tree) + validateCompanionDefs(tree) + sym } /** Enter a module symbol. The tree parameter can be either @@ -635,6 +664,7 @@ trait Namers extends MethodSynthesis { } else context.unit.error(tree.pos, "implicit classes must accept exactly one primary constructor parameter") } + validateCompanionDefs(tree) } // this logic is needed in case typer was interrupted half @@ -699,7 +729,7 @@ trait Namers extends MethodSynthesis { // } } - def moduleClassTypeCompleter(tree: Tree) = { + def moduleClassTypeCompleter(tree: ModuleDef) = { mkTypeCompleter(tree) { sym => val moduleSymbol = tree.symbol assert(moduleSymbol.moduleClass == sym, moduleSymbol.moduleClass) @@ -1545,18 +1575,11 @@ trait Namers extends MethodSynthesis { * call this method? */ def companionSymbolOf(original: Symbol, ctx: Context): Symbol = { - try { - original.companionSymbol orElse { - ctx.lookup(original.name.companionName, original.owner).suchThat(sym => - (original.isTerm || sym.hasModuleFlag) && - (sym isCoDefinedWith original) - ) - } - } - catch { - case e: InvalidCompanions => - ctx.unit.error(original.pos, e.getMessage) - NoSymbol + original.companionSymbol orElse { + ctx.lookup(original.name.companionName, original.owner).suchThat(sym => + (original.isTerm || sym.hasModuleFlag) && + (sym isCoDefinedWith original) + ) } } } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 3548657c04..dc73d0bc9f 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1790,26 +1790,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => } else owner.enclosingTopLevelClass /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ - def isCoDefinedWith(that: Symbol) = { - (this.rawInfo ne NoType) && - (this.effectiveOwner == that.effectiveOwner) && { - !this.effectiveOwner.isPackageClass || - (this.sourceFile eq null) || - (that.sourceFile eq null) || - (this.sourceFile == that.sourceFile) || { - // recognize companion object in separate file and fail, else compilation - // appears to succeed but highly opaque errors come later: see bug #1286 - if (this.sourceFile.path != that.sourceFile.path) { - // The cheaper check can be wrong: do the expensive normalization - // before failing. - if (this.sourceFile.canonicalPath != that.sourceFile.canonicalPath) - throw InvalidCompanions(this, that) - } - - false - } - } - } + def isCoDefinedWith(that: Symbol) = ( + (this.rawInfo ne NoType) + && (this.effectiveOwner == that.effectiveOwner) + && ( !this.effectiveOwner.isPackageClass + || (this.sourceFile eq null) + || (that.sourceFile eq null) + || (this.sourceFile.path == that.sourceFile.path) // Cheap possibly wrong check, then expensive normalization + || (this.sourceFile.canonicalPath == that.sourceFile.canonicalPath) + ) + ) /** The internal representation of classes and objects: * @@ -3202,13 +3192,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (settings.debug.value) printStackTrace() } - case class InvalidCompanions(sym1: Symbol, sym2: Symbol) extends Throwable({ - "Companions '" + sym1 + "' and '" + sym2 + "' must be defined in same file:\n" + - " Found in " + sym1.sourceFile.canonicalPath + " and " + sym2.sourceFile.canonicalPath - }) { - override def toString = getMessage - } - /** A class for type histories */ private sealed case class TypeHistory(var validFrom: Period, info: Type, prev: TypeHistory) { assert((prev eq null) || phaseId(validFrom) > phaseId(prev.validFrom), this) diff --git a/test/files/neg/t5031.check b/test/files/neg/t5031.check index 8983d8daf9..2f1090c321 100644 --- a/test/files/neg/t5031.check +++ b/test/files/neg/t5031.check @@ -1,5 +1,5 @@ -Id.scala:3: error: Companions 'class Test' and 'object Test' must be defined in same file: +package.scala:2: error: Companions 'class Test' and 'object Test' must be defined in same file: Found in t5031/package.scala and t5031/Id.scala -object Test - ^ + class Test + ^ one error found diff --git a/test/files/neg/t5031b.check b/test/files/neg/t5031b.check new file mode 100644 index 0000000000..3bc2284a4d --- /dev/null +++ b/test/files/neg/t5031b.check @@ -0,0 +1,5 @@ +b.scala:3: error: Companions 'class Bippy' and 'object Bippy' must be defined in same file: + Found in t5031b/a.scala and t5031b/b.scala +object Bippy + ^ +one error found diff --git a/test/files/neg/t5031b/a.scala b/test/files/neg/t5031b/a.scala new file mode 100644 index 0000000000..0ab9aa9769 --- /dev/null +++ b/test/files/neg/t5031b/a.scala @@ -0,0 +1,3 @@ +package foo + +class Bippy diff --git a/test/files/neg/t5031b/b.scala b/test/files/neg/t5031b/b.scala new file mode 100644 index 0000000000..bdef237af5 --- /dev/null +++ b/test/files/neg/t5031b/b.scala @@ -0,0 +1,3 @@ +package foo + +object Bippy -- cgit v1.2.3 From 6f78b501c226bd3c24c516801a23865531591ebe Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 9 Sep 2012 14:10:53 +0200 Subject: More tests for SI-6335. --- test/files/neg/t6335.check | 9 +++++++++ test/files/neg/t6335.scala | 7 +++++++ test/files/pos/t6335.scala | 16 +++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/files/neg/t6335.check create mode 100644 test/files/neg/t6335.scala (limited to 'test/files/neg') diff --git a/test/files/neg/t6335.check b/test/files/neg/t6335.check new file mode 100644 index 0000000000..1727a05eb2 --- /dev/null +++ b/test/files/neg/t6335.check @@ -0,0 +1,9 @@ +t6335.scala:6: error: method Z is defined twice + conflicting symbols both originated in file 't6335.scala' + implicit class Z[A](val i: A) { def zz = i } + ^ +t6335.scala:3: error: method X is defined twice + conflicting symbols both originated in file 't6335.scala' + implicit class X(val x: Int) { def xx = x } + ^ +two errors found diff --git a/test/files/neg/t6335.scala b/test/files/neg/t6335.scala new file mode 100644 index 0000000000..5c41e81ef5 --- /dev/null +++ b/test/files/neg/t6335.scala @@ -0,0 +1,7 @@ +object ImplicitClass { + def X(i: Int) {} + implicit class X(val x: Int) { def xx = x } + + def Z[A](i: A) {} + implicit class Z[A](val i: A) { def zz = i } +} \ No newline at end of file diff --git a/test/files/pos/t6335.scala b/test/files/pos/t6335.scala index a39c4f27f5..50e34092d1 100644 --- a/test/files/pos/t6335.scala +++ b/test/files/pos/t6335.scala @@ -1,11 +1,25 @@ -object E { +object E extends Z { def X = 3 implicit class X(val i: Int) { def xx = i } + + def Y(a: Any) = 0 + object Y + implicit class Y(val i: String) { def yy = i } + + implicit class Z(val i: Boolean) { def zz = i } +} + +trait Z { + def Z = 0 } object Test { import E._ 0.xx + + "".yy + + true.zz } -- cgit v1.2.3 From 24b0711f01dcb410ffd0454881f7a96073f92e16 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 9 Sep 2012 16:59:43 +0200 Subject: SI-6276 Warn on def or val that trivially loops infinitely --- .../scala/tools/nsc/typechecker/RefChecks.scala | 13 ++++++++++ test/files/neg/override.check | 2 +- test/files/neg/override.scala | 2 +- test/files/neg/t6276.check | 13 ++++++++++ test/files/neg/t6276.flags | 1 + test/files/neg/t6276.scala | 29 ++++++++++++++++++++++ 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t6276.check create mode 100644 test/files/neg/t6276.flags create mode 100644 test/files/neg/t6276.scala (limited to 'test/files/neg') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 166bb2d18c..dd4f13fd7b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1197,6 +1197,18 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case _ => } + // SI-6276 warn for `def foo = foo` or `val bar: X = bar`, which come up more frequently than you might think. + def checkInfiniteLoop(valOrDef: ValOrDefDef) { + val trivialInifiniteLoop = ( + !valOrDef.isErroneous + && !valOrDef.symbol.isValueParameter + && valOrDef.symbol.paramss.isEmpty + && valOrDef.rhs.hasSymbolWhich(_.accessedOrSelf == valOrDef.symbol) + ) + if (trivialInifiniteLoop) + unit.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively") + } + // Transformation ------------------------------------------------------------ /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */ @@ -1640,6 +1652,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) => checkDeprecatedOvers(tree) + checkInfiniteLoop(tree.asInstanceOf[ValOrDefDef]) if (settings.warnNullaryUnit.value) checkNullaryMethodReturnType(sym) if (settings.warnInaccessible.value) { diff --git a/test/files/neg/override.check b/test/files/neg/override.check index fc152cb3b1..8be98bf4d0 100644 --- a/test/files/neg/override.check +++ b/test/files/neg/override.check @@ -1,5 +1,5 @@ override.scala:9: error: overriding type T in trait A with bounds >: Int <: Int; type T in trait B with bounds >: String <: String has incompatible type - lazy val x : A with B = x + lazy val x : A with B = {println(""); x} ^ one error found diff --git a/test/files/neg/override.scala b/test/files/neg/override.scala index 3e589b52e3..7975516061 100755 --- a/test/files/neg/override.scala +++ b/test/files/neg/override.scala @@ -6,7 +6,7 @@ trait X { trait Y extends X { trait B { type T >: String <: String } - lazy val x : A with B = x + lazy val x : A with B = {println(""); x} n = "foo" } diff --git a/test/files/neg/t6276.check b/test/files/neg/t6276.check new file mode 100644 index 0000000000..8bd92cf293 --- /dev/null +++ b/test/files/neg/t6276.check @@ -0,0 +1,13 @@ +t6276.scala:4: error: method a does nothing other than call itself recursively + def a: Any = a // warn + ^ +t6276.scala:5: error: value b does nothing other than call itself recursively + val b: Any = b // warn + ^ +t6276.scala:10: error: method a does nothing other than call itself recursively + def a: Any = a // warn + ^ +t6276.scala:19: error: method a does nothing other than call itself recursively + def a = a // warn + ^ +four errors found diff --git a/test/files/neg/t6276.flags b/test/files/neg/t6276.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/t6276.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/t6276.scala b/test/files/neg/t6276.scala new file mode 100644 index 0000000000..8333618964 --- /dev/null +++ b/test/files/neg/t6276.scala @@ -0,0 +1,29 @@ +object Test { + def foo(a: Int, b: Int, c: Int) { + new { + def a: Any = a // warn + val b: Any = b // warn + } + + def method { + // method local + def a: Any = a // warn + } + + trait T { + def a: Any + } + + new T { + // inherited return type + def a = a // warn + } + + // no warnings below + new { + def a: Any = {println(""); a} + val b: Any = {println(""); b} + def c(i: Int): Any = c(i - 0) + } + } +} -- cgit v1.2.3 From e3b0c7abbf637dacce7bcd7b69d5655820e8e714 Mon Sep 17 00:00:00 2001 From: Simon Ochsenreither Date: Tue, 31 Jul 2012 01:02:07 +0200 Subject: SI-6162 Adds @deprecatedInheritance/@deprecatedOverriding These annotations are meant to warn from inheriting a class or from overriding a member, due to the reasons given in `msg`. The naming and placement of the methods is in line with @deprecated and @deprecatedName. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 11 +++++++++++ .../scala/tools/nsc/typechecker/Typers.scala | 9 +++++++++ src/library/scala/deprecatedInheritance.scala | 20 ++++++++++++++++++++ src/library/scala/deprecatedOverriding.scala | 19 +++++++++++++++++++ src/reflect/scala/reflect/internal/Definitions.scala | 2 ++ src/reflect/scala/reflect/internal/Symbols.scala | 12 ++++++++++++ test/files/neg/t6162-inheritance.check | 4 ++++ test/files/neg/t6162-inheritance.flags | 1 + test/files/neg/t6162-inheritance.scala | 4 ++++ test/files/neg/t6162-overriding.check | 4 ++++ test/files/neg/t6162-overriding.flags | 1 + test/files/neg/t6162-overriding.scala | 8 ++++++++ 12 files changed, 95 insertions(+) create mode 100644 src/library/scala/deprecatedInheritance.scala create mode 100644 src/library/scala/deprecatedOverriding.scala create mode 100644 test/files/neg/t6162-inheritance.check create mode 100644 test/files/neg/t6162-inheritance.flags create mode 100644 test/files/neg/t6162-inheritance.scala create mode 100644 test/files/neg/t6162-overriding.check create mode 100644 test/files/neg/t6162-overriding.flags create mode 100644 test/files/neg/t6162-overriding.scala (limited to 'test/files/neg') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 166bb2d18c..919250c562 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -430,6 +430,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R overrideError("cannot override a macro") } else { checkOverrideTypes() + checkOverrideDeprecated() if (settings.warnNullaryOverride.value) { if (other.paramss.isEmpty && !member.paramss.isEmpty) { unit.warning(member.pos, "non-nullary method overrides nullary method") @@ -508,6 +509,16 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } } } + + def checkOverrideDeprecated() { + if (other.hasDeprecatedOverridingAnnotation) { + val msg = + member.toString + member.locationString + " overrides " + other.toString + other.locationString + + ", but overriding this member is deprecated" + + other.deprecatedOverridingMessage.map(": " + _).getOrElse(".") + unit.deprecationWarning(member.pos, msg) + } + } } val opc = new overridingPairs.Cursor(clazz) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9cf5d42e00..df97e451d1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1577,6 +1577,15 @@ trait Typers extends Modes with Adaptations with Tags { if (psym.isFinal) pending += ParentFinalInheritanceError(parent, psym) + if (psym.hasDeprecatedInheritanceAnnotation) { + val sym = selfType.typeSymbol + val msg = + sym.toString + sym.locationString + " inherits from " + psym.toString + psym.locationString + + ", but inheriting from that class is deprecated" + + psym.deprecatedInheritanceMessage.map(": " + _).getOrElse(".") + unit.deprecationWarning(sym.pos, msg) + } + if (psym.isSealed && !phase.erasedTypes) if (context.unit.source.file == psym.sourceFile) psym addChild context.owner diff --git a/src/library/scala/deprecatedInheritance.scala b/src/library/scala/deprecatedInheritance.scala new file mode 100644 index 0000000000..c461f6737c --- /dev/null +++ b/src/library/scala/deprecatedInheritance.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2012, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +/** An annotation that designates that inheriting from a class is deprecated. + * + * This is usually done to warn about a non-final class being made final in a future version. + * Sub-classing such a class then generates a warning. + * + * @param message the message to print during compilation if the class was sub-classed + * @param since a string identifying the first version in which inheritance was deprecated + * @since 2.10 + */ +class deprecatedInheritance(message: String = "", since: String = "") extends annotation.StaticAnnotation \ No newline at end of file diff --git a/src/library/scala/deprecatedOverriding.scala b/src/library/scala/deprecatedOverriding.scala new file mode 100644 index 0000000000..9048d5d32d --- /dev/null +++ b/src/library/scala/deprecatedOverriding.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2012, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +/** An annotation that designates that overriding a member is deprecated. + * + * Overriding such a member in a sub-class then generates a warning. + * + * @param message the message to print during compilation if the member was overridden + * @param since a string identifying the first version in which overriding was deprecated + * @since 2.10 + */ +class deprecatedOverriding(message: String = "", since: String = "") extends annotation.StaticAnnotation \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index c21ebfe997..556fcee64f 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -940,6 +940,8 @@ trait Definitions extends api.StandardDefinitions { lazy val CloneableAttr = requiredClass[scala.annotation.cloneable] lazy val DeprecatedAttr = requiredClass[scala.deprecated] lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName] + lazy val DeprecatedInheritanceAttr = requiredClass[scala.deprecatedInheritance] + lazy val DeprecatedOverridingAttr = requiredClass[scala.deprecatedOverriding] lazy val NativeAttr = requiredClass[scala.native] lazy val RemoteAttr = requiredClass[scala.remote] lazy val ScalaInlineClass = requiredClass[scala.inline] diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 3548657c04..e75079a6ff 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -697,6 +697,18 @@ trait Symbols extends api.Symbols { self: SymbolTable => def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0) def deprecationVersion = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 1) def deprecatedParamName = getAnnotation(DeprecatedNameAttr) flatMap (_ symbolArg 0) + def hasDeprecatedInheritanceAnnotation + = hasAnnotation(DeprecatedInheritanceAttr) + def deprecatedInheritanceMessage + = getAnnotation(DeprecatedInheritanceAttr) flatMap (_ stringArg 0) + def deprecatedInheritanceVersion + = getAnnotation(DeprecatedInheritanceAttr) flatMap (_ stringArg 1) + def hasDeprecatedOverridingAnnotation + = hasAnnotation(DeprecatedOverridingAttr) + def deprecatedOverridingMessage + = getAnnotation(DeprecatedOverridingAttr) flatMap (_ stringArg 0) + def deprecatedOverridingVersion + = getAnnotation(DeprecatedOverridingAttr) flatMap (_ stringArg 1) // !!! when annotation arguments are not literal strings, but any sort of // assembly of strings, there is a fair chance they will turn up here not as diff --git a/test/files/neg/t6162-inheritance.check b/test/files/neg/t6162-inheritance.check new file mode 100644 index 0000000000..69112d7f86 --- /dev/null +++ b/test/files/neg/t6162-inheritance.check @@ -0,0 +1,4 @@ +t6162-inheritance.scala:4: error: class SubFoo inherits from class Foo, but inheriting from that class is deprecated: `Foo` will be made final in a future version. +class SubFoo extends Foo + ^ +one error found \ No newline at end of file diff --git a/test/files/neg/t6162-inheritance.flags b/test/files/neg/t6162-inheritance.flags new file mode 100644 index 0000000000..65faf53579 --- /dev/null +++ b/test/files/neg/t6162-inheritance.flags @@ -0,0 +1 @@ +-Xfatal-warnings -deprecation \ No newline at end of file diff --git a/test/files/neg/t6162-inheritance.scala b/test/files/neg/t6162-inheritance.scala new file mode 100644 index 0000000000..67bd4466c3 --- /dev/null +++ b/test/files/neg/t6162-inheritance.scala @@ -0,0 +1,4 @@ +@deprecatedInheritance("`Foo` will be made final in a future version.", "2.10.0") +class Foo + +class SubFoo extends Foo \ No newline at end of file diff --git a/test/files/neg/t6162-overriding.check b/test/files/neg/t6162-overriding.check new file mode 100644 index 0000000000..14221ddc63 --- /dev/null +++ b/test/files/neg/t6162-overriding.check @@ -0,0 +1,4 @@ +t6162-overriding.scala:7: error: method bar in class SubBar overrides method bar in class Bar, but overriding this member is deprecated: `bar` will be made private in a future version. + override def bar = 43 + ^ +one error found diff --git a/test/files/neg/t6162-overriding.flags b/test/files/neg/t6162-overriding.flags new file mode 100644 index 0000000000..65faf53579 --- /dev/null +++ b/test/files/neg/t6162-overriding.flags @@ -0,0 +1 @@ +-Xfatal-warnings -deprecation \ No newline at end of file diff --git a/test/files/neg/t6162-overriding.scala b/test/files/neg/t6162-overriding.scala new file mode 100644 index 0000000000..4907dbb075 --- /dev/null +++ b/test/files/neg/t6162-overriding.scala @@ -0,0 +1,8 @@ +class Bar { + @deprecatedOverriding("`bar` will be made private in a future version.", "2.10.0") + def bar = 42 +} + +class SubBar extends Bar { + override def bar = 43 +} \ No newline at end of file -- cgit v1.2.3 From 028de5d78225d3eb4d41d87bdbe56b7631ef76d1 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 10 Sep 2012 23:25:40 +0200 Subject: Rescues @deprecated{Inheritance, Overriding} While they ought to be generalized to aribirary modifier changes before being offered in the standard library, the opportunity to use them in 2.10 is too important to pass up. So for now, they're private[scala]. En route: - made the error messages more concise - fix positioning of inheritance error - improve test coverage --- .../scala/tools/nsc/typechecker/RefChecks.scala | 6 ++---- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 9 +++------ src/library/scala/deprecatedInheritance.scala | 4 +++- src/library/scala/deprecatedOverriding.scala | 4 +++- test/files/neg/t6162-inheritance.check | 10 ++++++++-- test/files/neg/t6162-inheritance.scala | 17 ++++++++++++++++- test/files/neg/t6162-overriding.check | 7 +++++-- test/files/neg/t6162-overriding.scala | 11 ++++++++++- 8 files changed, 50 insertions(+), 18 deletions(-) (limited to 'test/files/neg') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 919250c562..b9ff04c9df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -512,10 +512,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def checkOverrideDeprecated() { if (other.hasDeprecatedOverridingAnnotation) { - val msg = - member.toString + member.locationString + " overrides " + other.toString + other.locationString + - ", but overriding this member is deprecated" + - other.deprecatedOverridingMessage.map(": " + _).getOrElse(".") + val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse "" + val msg = s"overriding ${other.fullLocationString} is deprecated$suffix" unit.deprecationWarning(member.pos, msg) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index df97e451d1..3913c4f815 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1578,12 +1578,9 @@ trait Typers extends Modes with Adaptations with Tags { pending += ParentFinalInheritanceError(parent, psym) if (psym.hasDeprecatedInheritanceAnnotation) { - val sym = selfType.typeSymbol - val msg = - sym.toString + sym.locationString + " inherits from " + psym.toString + psym.locationString + - ", but inheriting from that class is deprecated" + - psym.deprecatedInheritanceMessage.map(": " + _).getOrElse(".") - unit.deprecationWarning(sym.pos, msg) + val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse "" + val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix" + unit.deprecationWarning(parent.pos, msg) } if (psym.isSealed && !phase.erasedTypes) diff --git a/src/library/scala/deprecatedInheritance.scala b/src/library/scala/deprecatedInheritance.scala index c461f6737c..4dd847c7b2 100644 --- a/src/library/scala/deprecatedInheritance.scala +++ b/src/library/scala/deprecatedInheritance.scala @@ -16,5 +16,7 @@ package scala * @param message the message to print during compilation if the class was sub-classed * @param since a string identifying the first version in which inheritance was deprecated * @since 2.10 + * @see [[scala.deprecatedInheritance]] */ -class deprecatedInheritance(message: String = "", since: String = "") extends annotation.StaticAnnotation \ No newline at end of file +private[scala] // for now, this needs to be generalized to communicate other modifier deltas +class deprecatedInheritance(message: String = "", since: String = "") extends annotation.StaticAnnotation diff --git a/src/library/scala/deprecatedOverriding.scala b/src/library/scala/deprecatedOverriding.scala index 9048d5d32d..566cb59431 100644 --- a/src/library/scala/deprecatedOverriding.scala +++ b/src/library/scala/deprecatedOverriding.scala @@ -15,5 +15,7 @@ package scala * @param message the message to print during compilation if the member was overridden * @param since a string identifying the first version in which overriding was deprecated * @since 2.10 + * @see [[scala.deprecatedInheritance]] */ -class deprecatedOverriding(message: String = "", since: String = "") extends annotation.StaticAnnotation \ No newline at end of file +private[scala] // for the same reasons as deprecatedInheritance +class deprecatedOverriding(message: String = "", since: String = "") extends annotation.StaticAnnotation diff --git a/test/files/neg/t6162-inheritance.check b/test/files/neg/t6162-inheritance.check index 69112d7f86..a7d3cc3238 100644 --- a/test/files/neg/t6162-inheritance.check +++ b/test/files/neg/t6162-inheritance.check @@ -1,4 +1,10 @@ -t6162-inheritance.scala:4: error: class SubFoo inherits from class Foo, but inheriting from that class is deprecated: `Foo` will be made final in a future version. +t6162-inheritance.scala:6: error: inheritance from class Foo in package t6126 is deprecated: `Foo` will be made final in a future version. class SubFoo extends Foo + ^ +t6162-inheritance.scala:11: error: inheritance from trait T in package t6126 is deprecated +object SubT extends T + ^ +t6162-inheritance.scala:17: error: inheritance from trait S in package t6126 is deprecated + new S { ^ -one error found \ No newline at end of file +three errors found diff --git a/test/files/neg/t6162-inheritance.scala b/test/files/neg/t6162-inheritance.scala index 67bd4466c3..7b47b9285a 100644 --- a/test/files/neg/t6162-inheritance.scala +++ b/test/files/neg/t6162-inheritance.scala @@ -1,4 +1,19 @@ +package scala.t6126 + @deprecatedInheritance("`Foo` will be made final in a future version.", "2.10.0") class Foo -class SubFoo extends Foo \ No newline at end of file +class SubFoo extends Foo + +@deprecatedInheritance() +trait T + +object SubT extends T + +@deprecatedInheritance() +trait S + +object O { + new S { + } +} diff --git a/test/files/neg/t6162-overriding.check b/test/files/neg/t6162-overriding.check index 14221ddc63..e774888d36 100644 --- a/test/files/neg/t6162-overriding.check +++ b/test/files/neg/t6162-overriding.check @@ -1,4 +1,7 @@ -t6162-overriding.scala:7: error: method bar in class SubBar overrides method bar in class Bar, but overriding this member is deprecated: `bar` will be made private in a future version. +t6162-overriding.scala:14: error: overriding method bar in class Bar is deprecated: `bar` will be made private in a future version. override def bar = 43 ^ -one error found +t6162-overriding.scala:15: error: overriding method baz in class Bar is deprecated + override def baz = 43 + ^ +two errors found diff --git a/test/files/neg/t6162-overriding.scala b/test/files/neg/t6162-overriding.scala index 4907dbb075..4cab0c2dee 100644 --- a/test/files/neg/t6162-overriding.scala +++ b/test/files/neg/t6162-overriding.scala @@ -1,8 +1,17 @@ +package scala.t6162 + class Bar { @deprecatedOverriding("`bar` will be made private in a future version.", "2.10.0") def bar = 42 + + @deprecatedOverriding() + def baz = 42 + + def baz(a: Any) = 0 } class SubBar extends Bar { override def bar = 43 -} \ No newline at end of file + override def baz = 43 + override def baz(a: Any) = 43 // okay +} -- cgit v1.2.3 From 1776337253744482b7116326413de5e260a5984e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 11 Sep 2012 22:03:58 +0200 Subject: Avoid spurious warning for `def foo = x.foo`. Followup to SI-6276. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 7 ++++++- test/files/neg/t6276.check | 16 +++++++++++----- test/files/neg/t6276.scala | 17 ++++++++++++++++- 3 files changed, 33 insertions(+), 7 deletions(-) (limited to 'test/files/neg') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index f1f2794a95..d515934c58 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1208,11 +1208,16 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R // SI-6276 warn for `def foo = foo` or `val bar: X = bar`, which come up more frequently than you might think. def checkInfiniteLoop(valOrDef: ValOrDefDef) { + def callsSelf = valOrDef.rhs match { + case t @ (Ident(_) | Select(This(_), _)) => + t hasSymbolWhich (_.accessedOrSelf == valOrDef.symbol) + case _ => false + } val trivialInifiniteLoop = ( !valOrDef.isErroneous && !valOrDef.symbol.isValueParameter && valOrDef.symbol.paramss.isEmpty - && valOrDef.rhs.hasSymbolWhich(_.accessedOrSelf == valOrDef.symbol) + && callsSelf ) if (trivialInifiniteLoop) unit.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively") diff --git a/test/files/neg/t6276.check b/test/files/neg/t6276.check index 8bd92cf293..0b3dfa5531 100644 --- a/test/files/neg/t6276.check +++ b/test/files/neg/t6276.check @@ -1,13 +1,19 @@ -t6276.scala:4: error: method a does nothing other than call itself recursively +t6276.scala:4: error: method a in class C does nothing other than call itself recursively def a: Any = a // warn ^ -t6276.scala:5: error: value b does nothing other than call itself recursively +t6276.scala:5: error: value b in class C does nothing other than call itself recursively val b: Any = b // warn ^ -t6276.scala:10: error: method a does nothing other than call itself recursively +t6276.scala:7: error: method c in class C does nothing other than call itself recursively + def c: Any = this.c // warn + ^ +t6276.scala:8: error: method d in class C does nothing other than call itself recursively + def d: Any = C.this.d // warn + ^ +t6276.scala:13: error: method a does nothing other than call itself recursively def a: Any = a // warn ^ -t6276.scala:19: error: method a does nothing other than call itself recursively +t6276.scala:22: error: method a does nothing other than call itself recursively def a = a // warn ^ -four errors found +6 errors found diff --git a/test/files/neg/t6276.scala b/test/files/neg/t6276.scala index 8333618964..bd0a473f71 100644 --- a/test/files/neg/t6276.scala +++ b/test/files/neg/t6276.scala @@ -1,8 +1,11 @@ object Test { def foo(a: Int, b: Int, c: Int) { - new { + class C { def a: Any = a // warn val b: Any = b // warn + + def c: Any = this.c // warn + def d: Any = C.this.d // warn } def method { @@ -25,5 +28,17 @@ object Test { val b: Any = {println(""); b} def c(i: Int): Any = c(i - 0) } + + class D { + def other: D = null + def foo: Any = other.foo + } + + class E { + def foo: Any = 0 + class D extends E { + override def foo: Any = E.this.foo + } + } } } -- cgit v1.2.3