From e7aadd00392a512ddcf53396d489f0f17bfac231 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 4 Apr 2013 16:51:41 -0700 Subject: SI-7330 better error when pattern isn't a value Somehow an applied type managed to sneak past the type checker in pattern mode. Patterns must be values, though. `case C[_] =>` was probably meant to be `case _: C[_] =>` Advice is dispensed accordingly. (Generalizing the existing advice machinery.) --- .../tools/nsc/typechecker/ContextErrors.scala | 35 +++++----------------- .../tools/nsc/typechecker/TypeDiagnostics.scala | 18 +++++++++++ .../scala/tools/nsc/typechecker/Typers.scala | 5 +++- 3 files changed, 30 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 4bf7f78167..49049f110d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -589,6 +589,10 @@ trait ContextErrors { setError(tree) } + // typedPattern + def PatternMustBeValue(pat: Tree, pt: Type) = + issueNormalTypeError(pat, s"pattern must be a value: $pat"+ typePatternAdvice(pat.tpe.typeSymbol, pt.typeSymbol)) + // SelectFromTypeTree def TypeSelectionFromVolatileTypeError(tree: Tree, qual: Tree) = { val hiBound = qual.tpe.bounds.hi @@ -920,33 +924,10 @@ trait ContextErrors { def IncompatibleScrutineeTypeError(tree: Tree, pattp: Type, pt: Type) = issueNormalTypeError(tree, "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt)) - def PatternTypeIncompatibleWithPtError2(pat: Tree, pt1: Type, pt: Type) = { - def errMsg = { - val sym = pat.tpe.typeSymbol - val clazz = sym.companionClass - val addendum = ( - if (sym.isModuleClass && clazz.isCaseClass && (clazz isSubClass pt1.typeSymbol)) { - // TODO: move these somewhere reusable. - val typeString = clazz.typeParams match { - case Nil => "" + clazz.name - case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") - } - val caseString = ( - clazz.caseFieldAccessors - map (_ => "_") // could use the actual param names here - mkString (clazz.name + "(", ",", ")") - ) - ( - "\nNote: if you intended to match against the class, try `case _: " + - typeString + "` or `case " + caseString + "`" - ) - } - else "" - ) - "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt) + addendum - } - issueNormalTypeError(pat, errMsg) - } + def PatternTypeIncompatibleWithPtError2(pat: Tree, pt1: Type, pt: Type) = + issueNormalTypeError(pat, + "pattern type is incompatible with expected type"+ foundReqMsg(pat.tpe, pt) + + typePatternAdvice(pat.tpe.typeSymbol, pt1.typeSymbol)) def PolyAlternativeError(tree: Tree, argtypes: List[Type], sym: Symbol, err: PolyAlternativeErrorKind.ErrorType) = { import PolyAlternativeErrorKind._ diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 9376cb5237..4950a7efef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -286,6 +286,24 @@ trait TypeDiagnostics { ) } + def typePatternAdvice(sym: Symbol, ptSym: Symbol) = { + val clazz = if (sym.isModuleClass) sym.companionClass else sym + val caseString = + if (clazz.isCaseClass && (clazz isSubClass ptSym)) + ( clazz.caseFieldAccessors + map (_ => "_") // could use the actual param names here + mkString (s"`case ${clazz.name}(", ",", ")`") + ) + else + "`case _: " + (clazz.typeParams match { + case Nil => "" + clazz.name + case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") + })+ "`" + + "\nNote: if you intended to match against the class, try "+ caseString + + } + case class TypeDiag(tp: Type, sym: Symbol) extends Ordered[TypeDiag] { // save the name because it will be mutated until it has been // distinguished from the other types in the same error message diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 34ba8b46f9..7169f93f2b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5697,7 +5697,10 @@ trait Typers extends Modes with Adaptations with Tags { // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich, // but arbitrary conversions (in adapt) are disabled // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this? - typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) + typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) match { + case tpt if tpt.isType => PatternMustBeValue(tpt, pt); tpt + case pat => pat + } } /** Types a (fully parameterized) type tree */ -- cgit v1.2.3 From 9d98b6d2ad68ceefce5278f8b980d1a05b3fefd4 Mon Sep 17 00:00:00 2001 From: Eugene Vigdorchik Date: Fri, 12 Apr 2013 15:46:25 +0400 Subject: Interactive scaladoc: mark new typer run when done. As of now, when backgroundCompile is in the same typer run as scaladoc-fetching logic, typer is confused with stale symbols. --- src/compiler/scala/tools/nsc/interactive/Global.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 1f2245abb5..c63cca9b88 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -856,9 +856,9 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") /** Implements CompilerControl.askDocComment */ private[interactive] def getDocComment(sym: Symbol, source: SourceFile, site: Symbol, fragments: List[(Symbol,SourceFile)], response: Response[(String, String, Position)]) { - informIDE(s"getDocComment $sym at $source site $site") + informIDE(s"getDocComment $sym at $source, site $site") respond(response) { - withTempUnits(fragments.toList.unzip._2){ units => + withTempUnits(fragments.unzip._2){ units => for((sym, src) <- fragments) { val mirror = findMirrorSymbol(sym, units(src)) if (mirror ne NoSymbol) forceDocComment(mirror, units(src)) @@ -871,6 +871,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } } + // New typer run to remove temp units and drop per-run caches that might refer to symbols entered from temp units. + newTyperRun() } def stabilizedType(tree: Tree): Type = tree match { -- cgit v1.2.3 From 8556ca04c466cd7f7412465f02c52b764104f736 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 18 Apr 2013 11:58:30 -0700 Subject: Quiet down overloaded implicit warning. Apparently implicit classes product both a method symbol and a module symbol, both of which are marked implicit, which left this warning code believing there was an overloaded implicit method. --- src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 9 +++++---- test/files/pos/xlint1.flags | 1 + test/files/pos/xlint1.scala | 13 +++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 test/files/pos/xlint1.flags create mode 100644 test/files/pos/xlint1.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index b9fdd7280e..9a9ca6a654 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -135,7 +135,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } if (settings.lint.value) { clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => - val alts = clazz.info.decl(sym.name).alternatives + // implicit classes leave both a module symbol and a method symbol as residue + val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) if (alts.size > 1) alts foreach (x => unit.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds")) } @@ -1373,12 +1374,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans */ private def checkMigration(sym: Symbol, pos: Position) = { if (sym.hasMigrationAnnotation) { - val changed = try + val changed = try settings.Xmigration.value < ScalaVersion(sym.migrationVersion.get) catch { - case e : NumberFormatException => + case e : NumberFormatException => unit.warning(pos, s"${sym.fullLocationString} has an unparsable version number: ${e.getMessage()}") - // if we can't parse the format on the migration annotation just conservatively assume it changed + // if we can't parse the format on the migration annotation just conservatively assume it changed true } if (changed) diff --git a/test/files/pos/xlint1.flags b/test/files/pos/xlint1.flags new file mode 100644 index 0000000000..7949c2afa2 --- /dev/null +++ b/test/files/pos/xlint1.flags @@ -0,0 +1 @@ +-Xlint -Xfatal-warnings diff --git a/test/files/pos/xlint1.scala b/test/files/pos/xlint1.scala new file mode 100644 index 0000000000..27936d8b14 --- /dev/null +++ b/test/files/pos/xlint1.scala @@ -0,0 +1,13 @@ +package object foo { + implicit class Bar[T](val x: T) extends AnyVal { + def bippy = 1 + } +} + +package foo { + object Baz { + def main(args: Array[String]): Unit = { + "abc".bippy + } + } +} -- cgit v1.2.3 From c1327dcd99a6ca84d2550b8e4894ec7ee5ee2420 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 20 Apr 2013 11:52:21 +0200 Subject: SI-6675 Avoid spurious warning about pattern bind arity. In 692372ce, we added a warning (under -Xlint) when binding a `TupleN` in to a single pattern binder, which wasn't allowed before 2.10.0, and more often than not represents a bug. However, that warning overstretched, and warned even when using a Tuple Pattern to bind to the elements of such a value. This commit checks for this case, and avoids the spurious warnings. A new test case is added for this case to go with the existing test for SI-6675: $ ./tools/partest-ack 6675 % tests-with-matching-paths ... 3 % tests-with-matching-code ... 2 # 3 tests to run. test/partest --show-diff --show-log \ test/files/neg/t6675-old-patmat.scala \ test/files/neg/t6675.scala \ test/files/pos/t6675.scala \ "" Testing individual files testing: [...]/files/pos/t6675.scala [ OK ] Testing individual files testing: [...]/files/neg/t6675-old-patmat.scala [ OK ] testing: [...]/files/neg/t6675.scala [ OK ] All of 3 tests were successful (elapsed time: 00:00:03) --- bincompat-forward.whitelist.conf | 4 ++++ src/compiler/scala/tools/nsc/matching/Patterns.scala | 2 +- src/compiler/scala/tools/nsc/transform/UnCurry.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 12 +++++++++--- .../scala/tools/nsc/typechecker/Typers.scala | 6 +++--- .../scala/tools/nsc/typechecker/Unapplies.scala | 5 +++-- src/reflect/scala/reflect/internal/TreeInfo.scala | 16 +++++++++++++++- test/files/pos/t6675.flags | 1 + test/files/pos/t6675.scala | 20 ++++++++++++++++++++ 9 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 test/files/pos/t6675.flags create mode 100644 test/files/pos/t6675.scala (limited to 'src') diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index c59d0a7dec..82f094bed7 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -422,6 +422,10 @@ filter { { matchName="scala.reflect.internal.Types#TypeVar.setInst" problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.TreeInfo.effectivePatternArity" + problemName=MissingMethodProblem } ] } diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index f116a7c4c7..ef41246af9 100644 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala @@ -402,7 +402,7 @@ trait Patterns extends ast.TreeDSL { case _ => toPats(args) } - def resTypes = analyzer.unapplyTypeList(unfn.pos, unfn.symbol, unfn.tpe, args.length) + def resTypes = analyzer.unapplyTypeList(unfn.pos, unfn.symbol, unfn.tpe, args) def resTypesString = resTypes match { case Nil => "Boolean" case xs => xs.mkString(", ") diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 2c191096d6..430129aaff 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -623,7 +623,7 @@ abstract class UnCurry extends InfoTransform val fn1 = withInPattern(false)(transform(fn)) val args1 = transformTrees(fn.symbol.name match { case nme.unapply => args - case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(fn.pos, fn.symbol, fn.tpe, args.length)) + case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(fn.pos, fn.symbol, fn.tpe, args)) case _ => sys.error("internal error: UnApply node has wrong symbol") }) treeCopy.UnApply(tree, fn1, args1) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 7161043dcf..54c6a524bc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -55,6 +55,10 @@ trait Infer extends Checkable { * * `formals` are the formal types before expanding a potential repeated parameter (must come last in `formals`, if at all) * + * @param nbSubPats The number of arguments to the extractor pattern + * @param effectiveNbSubPats `nbSubPats`, unless there is one sub-pattern which, after unwrapping + * bind patterns, is a Tuple pattern, in which case it is the number of + * elements. Used to issue warnings about binding a `TupleN` to a single value. * @throws TypeError when the unapply[Seq] definition is ill-typed * @returns (null, null) when the expected number of sub-patterns cannot be satisfied by the given extractor * @@ -84,7 +88,8 @@ trait Infer extends Checkable { * `T_1, ..., T_m, U, ..., U`. Here, `U` is repeated `n-m` times. * */ - def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, unappSym: Symbol): (List[Type], List[Type]) = { + def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, + unappSym: Symbol, effectiveNbSubPats: Int): (List[Type], List[Type]) = { val isUnapplySeq = unappSym.name == nme.unapplySeq val booleanExtractor = resTp.typeSymbolDirect == BooleanClass @@ -114,8 +119,9 @@ trait Infer extends Checkable { else if (optionArgs.nonEmpty) if (nbSubPats == 1) { val productArity = productArgs.size - if (productArity > 1 && settings.lint.value) - global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") + if (settings.lint.value && productArity > 1 && productArity != effectiveNbSubPats) + global.currentUnit.warning(pos, + s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") optionArgs } // TODO: update spec to reflect we allow any ProductN, not just TupleN diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d1d6feae97..64e0603851 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3480,8 +3480,8 @@ trait Typers extends Modes with Adaptations with Tags { else { val resTp = fun1.tpe.finalResultType.normalize val nbSubPats = args.length - - val (formals, formalsExpanded) = extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol) + val (formals, formalsExpanded) = + extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol, treeInfo.effectivePatternArity(args)) if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) else { val args1 = typedArgs(args, mode, formals, formalsExpanded) @@ -5297,7 +5297,7 @@ trait Typers extends Modes with Adaptations with Tags { def typedUnApply(tree: UnApply) = { val fun1 = typed(tree.fun) - val tpes = formalTypes(unapplyTypeList(tree.fun.pos, tree.fun.symbol, fun1.tpe, tree.args.length), tree.args.length) + val tpes = formalTypes(unapplyTypeList(tree.fun.pos, tree.fun.symbol, fun1.tpe, tree.args), tree.args.length) val args1 = map2(tree.args, tpes)(typedPattern) treeCopy.UnApply(tree, fun1, args1) setType pt } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index b51dc0ccd5..31c5a61a8c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -34,12 +34,13 @@ trait Unapplies extends ast.TreeDSL /** returns type list for return type of the extraction * @see extractorFormalTypes */ - def unapplyTypeList(pos: Position, ufn: Symbol, ufntpe: Type, nbSubPats: Int) = { + def unapplyTypeList(pos: Position, ufn: Symbol, ufntpe: Type, args: List[Tree]) = { assert(ufn.isMethod, ufn) + val nbSubPats = args.length //Console.println("utl "+ufntpe+" "+ufntpe.typeSymbol) ufn.name match { case nme.unapply | nme.unapplySeq => - val (formals, _) = extractorFormalTypes(pos, unapplyUnwrap(ufntpe), nbSubPats, ufn) + val (formals, _) = extractorFormalTypes(pos, unapplyUnwrap(ufntpe), nbSubPats, ufn, treeInfo.effectivePatternArity(args)) if (formals == null) throw new TypeError(s"$ufn of type $ufntpe cannot extract $nbSubPats sub-patterns") else formals case _ => throw new TypeError(ufn+" is not an unapply or unapplySeq") diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 3e74b5d22d..fa4441e513 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -17,7 +17,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType } + import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -515,6 +515,20 @@ abstract class TreeInfo { case _ => false } + /** + * {{{ + * //------------------------ => effectivePatternArity(args) + * case Extractor(a) => 1 + * case Extractor(a, b) => 2 + * case Extractor((a, b)) => 2 + * case Extractor(a @ (b, c)) => 2 + * }}} + */ + def effectivePatternArity(args: List[Tree]): Int = (args.map(unbind) match { + case Apply(fun, xs) :: Nil if isTupleSymbol(fun.symbol) => xs + case xs => xs + }).length + // used in the symbols for labeldefs and valdefs emitted by the pattern matcher // tailcalls, cps,... use this flag combination to detect translated matches diff --git a/test/files/pos/t6675.flags b/test/files/pos/t6675.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t6675.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/pos/t6675.scala b/test/files/pos/t6675.scala new file mode 100644 index 0000000000..f3bebea5be --- /dev/null +++ b/test/files/pos/t6675.scala @@ -0,0 +1,20 @@ +object LeftOrRight { + def unapply[A](value: Either[A, A]): Option[A] = value match { + case scala.Left(x) => Some(x) + case scala.Right(x) => Some(x) + } +} + +object Test { + (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { + case LeftOrRight(pair @ (a, b)) => a // false -Xlint warning: "extractor pattern binds a single value to a Product2 of type (Int, Int)" + } + + (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { + case LeftOrRight((a, b)) => a // false -Xlint warning: "extractor pattern binds a single value to a Product2 of type (Int, Int)" + } + + (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { + case LeftOrRight(a, b) => a // false -Xlint warning: "extractor pattern binds a single value to a Product2 of type (Int, Int)" + } +} -- cgit v1.2.3 From d3aa9a7639c3c26b42a8faa9566d0d43a998b723 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 22 Apr 2013 09:54:59 +0200 Subject: Warn on selection of vals from DelayedInit subclasses. Which are likely to yield null, if the program didn't start. This is a common source of confusion for people new to the language, as was seen during the Coursera course. The test case shows that the usage pattern within Specs2 won't generate these warnings. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 11 ++++++ test/files/neg/delayed-init-ref.check | 10 ++++++ test/files/neg/delayed-init-ref.flags | 1 + test/files/neg/delayed-init-ref.scala | 42 ++++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 test/files/neg/delayed-init-ref.check create mode 100644 test/files/neg/delayed-init-ref.flags create mode 100644 test/files/neg/delayed-init-ref.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index b9fdd7280e..293b36c4d8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1396,6 +1396,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = { + def isLikelyUninitialized = ( + (sym.owner isSubClass DelayedInitClass) + && !qual.tpe.isInstanceOf[ThisType] + && sym.accessedOrSelf.isVal + ) + if (settings.lint.value && isLikelyUninitialized) + unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") + } + private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( (otherSym != NoSymbol) && !otherSym.isProtected @@ -1595,6 +1605,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if(settings.Xmigration.value != NoScalaVersion) checkMigration(sym, tree.pos) checkCompileTimeOnly(sym, tree.pos) + checkDelayedInitSelect(qual, sym, tree.pos) if (sym eq NoSymbol) { unit.warning(tree.pos, "Select node has NoSymbol! " + tree + " / " + tree.tpe) diff --git a/test/files/neg/delayed-init-ref.check b/test/files/neg/delayed-init-ref.check new file mode 100644 index 0000000000..42ccabed1b --- /dev/null +++ b/test/files/neg/delayed-init-ref.check @@ -0,0 +1,10 @@ +delayed-init-ref.scala:17: error: Selecting value vall from object O, which extends scala.DelayedInit, is likely to yield an uninitialized value + println(O.vall) // warn + ^ +delayed-init-ref.scala:19: error: Selecting value vall from object O, which extends scala.DelayedInit, is likely to yield an uninitialized value + println(vall) // warn + ^ +delayed-init-ref.scala:40: error: Selecting value foo from trait UserContext, which extends scala.DelayedInit, is likely to yield an uninitialized value + println({locally(()); this}.foo) // warn (spurious, but we can't discriminate) + ^ +three errors found diff --git a/test/files/neg/delayed-init-ref.flags b/test/files/neg/delayed-init-ref.flags new file mode 100644 index 0000000000..7949c2afa2 --- /dev/null +++ b/test/files/neg/delayed-init-ref.flags @@ -0,0 +1 @@ +-Xlint -Xfatal-warnings diff --git a/test/files/neg/delayed-init-ref.scala b/test/files/neg/delayed-init-ref.scala new file mode 100644 index 0000000000..f2aa804e28 --- /dev/null +++ b/test/files/neg/delayed-init-ref.scala @@ -0,0 +1,42 @@ +trait T { + val traitVal = "" +} + +object O extends App with T { + val vall = "" + lazy val lazyy = "" + def deff = "" + + println(vall) // no warn + new { + println(vall) // no warn + } +} + +object Client { + println(O.vall) // warn + import O.vall + println(vall) // warn + + println(O.lazyy) // no warn + println(O.deff) // no warn + println(O.traitVal) // no warn +} + +// Delayed init usage pattern from Specs2 +// See: https://groups.google.com/d/msg/scala-sips/wP6dL8nIAQs/ogjoPE-MSVAJ +trait Before extends DelayedInit { + def before() + override def delayedInit(x: => Unit): Unit = { before; x } +} +object Spec { + trait UserContext extends Before { + def before() = () + val foo = "foo" + } + new UserContext { + println(foo) // no warn + println(this.foo) // no warn + println({locally(()); this}.foo) // warn (spurious, but we can't discriminate) + } +} -- cgit v1.2.3 From 0d2c7e9a3a3e2c5240ba3015b2a08d7a982f3427 Mon Sep 17 00:00:00 2001 From: Bjorn Regnell Date: Thu, 11 Apr 2013 01:32:44 +0300 Subject: SI-7355 Handle spaces in paths in Windows batch files. Changed "%1%" and %2% to "%~1" and %~2 to allow spaces in paths by surrounding quotes according to advice at: http://stackoverflow.com/questions/473117/pass-path-with-spaces-as-parameter-to-bat-file http://ss64.com/nt/syntax-args.html --- src/compiler/scala/tools/ant/templates/tool-windows.tmpl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl index 411a27b9c7..bd6cf561b9 100644 --- a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl @@ -14,8 +14,9 @@ set _LINE_TOOLCP= :another_param -if "%1%"=="-toolcp" ( - set _LINE_TOOLCP=%2% +rem Use "%~1" to handle spaces in paths. See http://ss64.com/nt/syntax-args.html +if "%~1"=="-toolcp" ( + set _LINE_TOOLCP=%~2 shift shift goto another_param -- cgit v1.2.3 From 62713964b96f64f9c0fd0070c89aa6571679856d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 23 Apr 2013 23:55:05 +0200 Subject: SI-7369 Avoid spurious unreachable warnings in patterns Unreachability analysis draws on the enumerated domain of types (e.g sealed subclasses + null, or true/false), and also looks at all stable identifier patterns tested for equality against the same 'slot' in a pattern. It was drawing the wrong conclusions about stable identifier patterns. Unlike the domain constants, two such values may hold the same value, so we can't assume that matching X precludes matching Y in the same slot in a subsequent case. For example: val X: Boolean = true; val Y: Boolean = true def m1(t1: Tuple1[Boolean]) = t1 match { case Tuple1(true) => case Tuple1(false) => case Tuple1(false) => // correctly unreachable } def m2(t1: Tuple1[Boolean]) = t1 match { case Tuple1(X) => case Tuple1(Y) => // spurious unreachable warning } // // Before // reachability, vars: V2: Boolean ::= true | false// Set(false, Y, X, true) // = x1._1 V1: (Boolean,) ::= null | ... // = x1 equality axioms: V2=true#4 \/ V2=false#5 /\ -V2=false#5 \/ -V2=Y#3 /\ -V2=false#5 \/ -V2=X#2 /\ -V2=false#5 \/ -V2=true#4 /\ -V2=Y#3 \/ -V2=X#2 /\ -V2=Y#3 \/ -V2=true#4 /\ -V2=X#2 \/ -V2=true#4 // // After // reachability, vars: V2: Boolean ::= true | false// Set(false, Y, X, true) // = x1._1 V1: (Boolean,) ::= null | ... // = x1 equality axioms: V2=true#4 \/ V2=false#5 /\ -V2=false#5 \/ -V2=true#4 --- .../scala/tools/nsc/transform/patmat/Logic.scala | 39 ++++++++++++++------ test/files/neg/t7369.check | 13 +++++++ test/files/neg/t7369.flags | 1 + test/files/neg/t7369.scala | 43 ++++++++++++++++++++++ test/files/pos/t7369.flags | 1 + test/files/pos/t7369.scala | 37 +++++++++++++++++++ 6 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 test/files/neg/t7369.check create mode 100644 test/files/neg/t7369.flags create mode 100644 test/files/neg/t7369.scala create mode 100644 test/files/pos/t7369.flags create mode 100644 test/files/pos/t7369.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 0fab48028e..dbe08315f4 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -385,18 +385,35 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { // else debug.patmat("NOT implies: "+(lower, upper)) - /** does V = C preclude V having value `other`? - (1) V = null is an exclusive assignment, - (2) V = A and V = B, for A and B value constants, are mutually exclusive unless A == B - we err on the safe side, for example: - - assume `val X = 1; val Y = 1`, then - (2: Int) match { case X => case Y => } - - V = 1 does not preclude V = Int, or V = Any, it could be said to preclude V = String, but we don't model that - - (3) for types we could try to do something fancy, but be conservative and just say no + /** Does V=A preclude V=B? + * + * (0) A or B must be in the domain to draw any conclusions. + * + * For example, knowing the the scrutinee is *not* true does not + * statically exclude it from being `X`, because that is an opaque + * Boolean. + * + * val X = true + * (true: Boolean) match { case true => case X } + * + * (1) V = null excludes assignment to any other constant (modulo point #0). This includes + * both values and type tests (which are both modelled here as `Const`) + * (2) V = A and V = B, for A and B domain constants, are mutually exclusive unless A == B + * + * (3) We only reason about test tests as being excluded by null assignments, otherwise we + * only consider value assignments. + * TODO: refine this, a == 0 excludes a: String, or `a: Int` excludes `a: String` + * (since no value can be of both types. See also SI-7211) + * + * NOTE: V = 1 does not preclude V = Int, or V = Any, it could be said to preclude + * V = String, but we don't model that. */ - def excludes(a: Const, b: Const): Boolean = - a != b && ((a == NullConst || b == NullConst) || (a.isValue && b.isValue)) + def excludes(a: Const, b: Const): Boolean = { + val bothInDomain = domain exists (d => d(a) && d(b)) + val eitherIsNull = a == NullConst || b == NullConst + val bothAreValues = a.isValue && b.isValue + bothInDomain && (eitherIsNull || bothAreValues) && (a != b) + } // if(r) debug.patmat("excludes : "+(a, a.tp, b, b.tp)) // else debug.patmat("NOT excludes: "+(a, b)) diff --git a/test/files/neg/t7369.check b/test/files/neg/t7369.check new file mode 100644 index 0000000000..4f101e145a --- /dev/null +++ b/test/files/neg/t7369.check @@ -0,0 +1,13 @@ +t7369.scala:6: error: unreachable code + case Tuple1(X) => // unreachable + ^ +t7369.scala:13: error: unreachable code + case Tuple1(true) => // unreachable + ^ +t7369.scala:31: error: unreachable code + case Tuple1(X) => // unreachable + ^ +t7369.scala:40: error: unreachable code + case Tuple1(null) => // unreachable + ^ +four errors found diff --git a/test/files/neg/t7369.flags b/test/files/neg/t7369.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t7369.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/neg/t7369.scala b/test/files/neg/t7369.scala new file mode 100644 index 0000000000..87ddfe98b7 --- /dev/null +++ b/test/files/neg/t7369.scala @@ -0,0 +1,43 @@ +object Test { + val X, Y = true + (null: Tuple1[Boolean]) match { + case Tuple1(X) => + case Tuple1(Y) => + case Tuple1(X) => // unreachable + case _ => + } + + (null: Tuple1[Boolean]) match { + case Tuple1(true) => + case Tuple1(false) => + case Tuple1(true) => // unreachable + case _ => + } +} + + +sealed abstract class B; +case object True extends B; +case object False extends B; + +object Test2 { + + val X: B = True + val Y: B = False + + (null: Tuple1[B]) match { + case Tuple1(X) => + case Tuple1(Y) => + case Tuple1(X) => // unreachable + case _ => + } +} + +object Test3 { + (null: Tuple1[B]) match { + case Tuple1(null) => + case Tuple1(True) => + case Tuple1(null) => // unreachable + case _ => + } +} diff --git a/test/files/pos/t7369.flags b/test/files/pos/t7369.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/pos/t7369.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/pos/t7369.scala b/test/files/pos/t7369.scala new file mode 100644 index 0000000000..2f31c93d29 --- /dev/null +++ b/test/files/pos/t7369.scala @@ -0,0 +1,37 @@ +object Test { + val X, Y = true + (null: Tuple1[Boolean]) match { + case Tuple1(X) => + case Tuple1(Y) => // unreachable + case _ => + } +} + + +sealed abstract class B; +case object True extends B; +case object False extends B; + +object Test2 { + + val X: B = True + val Y: B = False + + (null: Tuple1[B]) match { + case Tuple1(X) => + case Tuple1(Y) => // no warning + case _ => + } +} + +object Test3 { + val X, O = true + def classify(neighbourhood: (Boolean, Boolean, Boolean)): String = { + neighbourhood match { + case (X, X, X) => "middle" + case (X, X, O) => "right" + case (O, X, X) => "left" + case _ => throw new IllegalArgumentException("Invalid") + } + } +} \ No newline at end of file -- cgit v1.2.3 From 184cac8ebc23aaa912d760649f80484d3adbb2b2 Mon Sep 17 00:00:00 2001 From: Eugene Vigdorchik Date: Mon, 15 Apr 2013 13:07:04 +0400 Subject: SI-7367 scaladoc crash on constructing the model for annotations. Scaladoc only checks primary constructor when building annotation model. Here we instead find the constructor matching the annotation's symbol. Also change TreeFactory.makeTree to return TreeEntity rather than Option[TreeEntity] and force the caller check for EmptyTree. --- .../scala/tools/nsc/doc/model/ModelFactory.scala | 43 ++++++++++++---------- .../scala/tools/nsc/doc/model/TreeFactory.scala | 13 +++---- test/scaladoc/run/SI-7367.check | 1 + test/scaladoc/run/SI-7367.scala | 25 +++++++++++++ 4 files changed, 55 insertions(+), 27 deletions(-) create mode 100755 test/scaladoc/run/SI-7367.check create mode 100755 test/scaladoc/run/SI-7367.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 0a469c9227..d9b173bc43 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -897,32 +897,36 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } } - /** */ def makeAnnotation(annot: AnnotationInfo): scala.tools.nsc.doc.model.Annotation = { val aSym = annot.symbol new EntityImpl(aSym, makeTemplate(aSym.owner)) with scala.tools.nsc.doc.model.Annotation { lazy val annotationClass = makeTemplate(annot.symbol) - val arguments = { // lazy - def noParams = annot.args map { _ => None } - val params: List[Option[ValueParam]] = annotationClass match { + val arguments = { + val paramsOpt: Option[List[ValueParam]] = annotationClass match { case aClass: DocTemplateEntity with Class => - (aClass.primaryConstructor map { _.valueParams.head }) match { - case Some(vps) => vps map { Some(_) } - case None => noParams + val constr = aClass.constructors collectFirst { + case c: MemberImpl if c.sym == annot.original.symbol => c } - case _ => noParams + constr flatMap (_.valueParams.headOption) + case _ => None } - assert(params.length == annot.args.length) - (params zip annot.args) flatMap { case (param, arg) => - makeTree(arg) match { - case Some(tree) => - Some(new ValueArgument { - def parameter = param + val argTrees = annot.args map makeTree + paramsOpt match { + case Some (params) => + params zip argTrees map { case (param, tree) => + new ValueArgument { + def parameter = Some(param) def value = tree - }) - case None => None - } + } + } + case None => + argTrees map { tree => + new ValueArgument { + def parameter = None + def value = tree + } + } } } } @@ -962,9 +966,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { sym.name == aSym.name && sym.isParamWithDefault ) - (unit.body find (t => isCorrespondingParam(t.symbol))) match { - case Some(ValDef(_,_,_,rhs)) => makeTree(rhs) - case _ => None + unit.body find (t => isCorrespondingParam(t.symbol)) collect { + case ValDef(_,_,_,rhs) if rhs ne EmptyTree => makeTree(rhs) } case _ => None } diff --git a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala index bd7534ded4..fdad84d0bc 100755 --- a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala @@ -19,7 +19,7 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => val global: Global import global._ - def makeTree(rhs: Tree): Option[TreeEntity] = { + def makeTree(rhs: Tree): TreeEntity = { var expr = new StringBuilder var refs = new immutable.TreeMap[Int, (Entity, Int)] // start, (Entity to be linked to , end) @@ -80,17 +80,16 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => traverser.traverse(rhs) - Some(new TreeEntity { + new TreeEntity { val expression = expr.toString val refEntity = refs - }) + } } - case pos: OffsetPosition => - Some(new TreeEntity { + case _ => + new TreeEntity { val expression = rhs.toString val refEntity = new immutable.TreeMap[Int, (Entity, Int)] - }) - case _ => None + } } } } diff --git a/test/scaladoc/run/SI-7367.check b/test/scaladoc/run/SI-7367.check new file mode 100755 index 0000000000..3925a0d464 --- /dev/null +++ b/test/scaladoc/run/SI-7367.check @@ -0,0 +1 @@ +Done. \ No newline at end of file diff --git a/test/scaladoc/run/SI-7367.scala b/test/scaladoc/run/SI-7367.scala new file mode 100755 index 0000000000..6e5a317932 --- /dev/null +++ b/test/scaladoc/run/SI-7367.scala @@ -0,0 +1,25 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + override def code = """ + class annot() extends annotation.StaticAnnotation { + def this(a: Any) = this() + } + + @annot(0) + class B + """ + + def scaladocSettings = "" + + def testModel(root: Package) = { + import access._ + val annotations = root._class("B").annotations + assert(annotations.size == 1) + assert(annotations(0).annotationClass == root._class("annot")) + val args = annotations(0).arguments + assert(args.size == 1) + assert(args(0).value.expression == "0") + } +} -- cgit v1.2.3 From 8448beb2ad6346d1d04a1feedb3de4d77f61024d Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 24 Apr 2013 12:22:44 -0700 Subject: SI-6943 warn on value class miscomparison. There's a very dangerous situation running around when you combine universal equality with value classes: // All over your code val x = "abc" if (x == "abc") ... // Hey let's make x a value class val x = new ValueClass("abc") // Uh-oh There was until now no warning when comparing a value class with something else. Now there is. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 32 ++++++++++++++------ test/files/neg/t5663-badwarneq.check | 34 +++++++++++++++++----- test/files/neg/t5663-badwarneq.scala | 18 ++++++++++++ 3 files changed, 67 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 9a9ca6a654..7f8aeceeec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1102,12 +1102,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val s = fn.symbol (s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=) } + def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) + // Whether the operands+operator represent a warnable combo (assuming anyrefs) // Looking for comparisons performed with ==/!= in combination with either an // equals method inherited from Object or a case class synthetic equals (for // which we know the logic.) def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) + def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) @@ -1122,6 +1125,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // unused def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size) val nullCount = onSyms(_ filter (_ == NullClass) size) + def isNonsenseValueClassCompare = ( + !haveSubclassRelationship + && isUsingDefaultScalaOp + && isEitherValueClass + && !isCaseEquals + ) def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) @@ -1133,10 +1142,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def nonSensiblyNeq() = nonSensible("", false) def nonSensiblyNew() = nonSensibleWarning("a fresh object", false) + def unrelatedMsg = name match { + case nme.EQ | nme.eq => "never compare equal" + case _ => "always compare unequal" + } def unrelatedTypes() = { - val msg = if (name == nme.EQ || name == nme.eq) - "never compare equal" else "always compare unequal" - unit.warning(pos, typesString + " are unrelated: they will most likely " + msg) + val weaselWord = if (isEitherValueClass) "" else " most likely" + unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") } if (nullCount == 2) // null == null @@ -1175,15 +1187,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + // warn if one but not the other is a derived value class + // this is especially important to enable transitioning from + // regular to value classes without silent failures. + if (isNonsenseValueClassCompare) + unrelatedTypes() // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean - if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { + else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { // better to have lubbed and lost def warnIfLubless(): Unit = { val common = global.lub(List(actual.tpe, receiver.tpe)) if (ObjectClass.tpe <:< common) unrelatedTypes() } - def eitherSubclasses = (actual isSubClass receiver) || (receiver isSubClass actual) // warn if actual has a case parent that is not same as receiver's; // if actual is not a case, then warn if no common supertype, as below if (isCaseEquals) { @@ -1196,14 +1212,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans //else // if a class, it must be super to thisCase (and receiver) since not <: thisCase if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() - else if (!eitherSubclasses) warnIfLubless() + else if (!haveSubclassRelationship) warnIfLubless() case _ => } } - else if (actual isSubClass receiver) () - else if (receiver isSubClass actual) () // warn only if they have no common supertype below Object - else { + else if (!haveSubclassRelationship) { warnIfLubless() } } diff --git a/test/files/neg/t5663-badwarneq.check b/test/files/neg/t5663-badwarneq.check index 00c2234e9d..242be8de68 100644 --- a/test/files/neg/t5663-badwarneq.check +++ b/test/files/neg/t5663-badwarneq.check @@ -1,22 +1,40 @@ -t5663-badwarneq.scala:42: error: comparing case class values of types Some[Int] and None.type using `==' will always yield false +t5663-badwarneq.scala:47: error: comparing case class values of types Some[Int] and None.type using `==' will always yield false println(new Some(1) == None) // Should complain on type, was: spuriously complains on fresh object ^ -t5663-badwarneq.scala:43: error: comparing case class values of types Some[Int] and Thing using `==' will always yield false +t5663-badwarneq.scala:48: error: comparing case class values of types Some[Int] and Thing using `==' will always yield false println(Some(1) == new Thing(1)) // Should complain on type, was: spuriously complains on fresh object ^ -t5663-badwarneq.scala:51: error: ThingOne and Thingy are unrelated: they will most likely never compare equal +t5663-badwarneq.scala:56: error: ThingOne and Thingy are unrelated: they will most likely never compare equal println(t1 == t2) // true, but apparently unrelated, a compromise warning ^ -t5663-badwarneq.scala:52: error: ThingThree and Thingy are unrelated: they will most likely never compare equal +t5663-badwarneq.scala:57: error: ThingThree and Thingy are unrelated: they will most likely never compare equal println(t4 == t2) // true, complains because ThingThree is final and Thingy not a subclass, stronger claim than unrelated ^ -t5663-badwarneq.scala:55: error: comparing case class values of types ThingTwo and Some[Int] using `==' will always yield false +t5663-badwarneq.scala:60: error: comparing case class values of types ThingTwo and Some[Int] using `==' will always yield false println(t3 == Some(1)) // false, warn on different cases ^ -t5663-badwarneq.scala:56: error: comparing values of types ThingOne and Cousin using `==' will always yield false +t5663-badwarneq.scala:61: error: comparing values of types ThingOne and Cousin using `==' will always yield false println(t1 == c) // should warn ^ -t5663-badwarneq.scala:64: error: comparing case class values of types Simple and SimpleSibling.type using `==' will always yield false +t5663-badwarneq.scala:69: error: comparing case class values of types Simple and SimpleSibling.type using `==' will always yield false println(new Simple() == SimpleSibling) // like Some(1) == None, but needn't be final case ^ -7 errors found +t5663-badwarneq.scala:72: error: ValueClass1 and Int are unrelated: they will never compare equal + println(new ValueClass1(5) == 5) // bad + ^ +t5663-badwarneq.scala:74: error: comparing values of types Int and ValueClass1 using `==' will always yield false + println(5 == new ValueClass1(5)) // bad + ^ +t5663-badwarneq.scala:78: error: ValueClass2[String] and String are unrelated: they will never compare equal + println(new ValueClass2("abc") == "abc") // bad + ^ +t5663-badwarneq.scala:79: error: ValueClass2[Int] and ValueClass1 are unrelated: they will never compare equal + println(new ValueClass2(5) == new ValueClass1(5)) // bad - different value classes + ^ +t5663-badwarneq.scala:81: error: comparing values of types ValueClass3 and ValueClass2[Int] using `==' will always yield false + println(ValueClass3(5) == new ValueClass2(5)) // bad + ^ +t5663-badwarneq.scala:82: error: comparing values of types ValueClass3 and Int using `==' will always yield false + println(ValueClass3(5) == 5) // bad + ^ +13 errors found diff --git a/test/files/neg/t5663-badwarneq.scala b/test/files/neg/t5663-badwarneq.scala index 56ec389c03..3c0376914e 100644 --- a/test/files/neg/t5663-badwarneq.scala +++ b/test/files/neg/t5663-badwarneq.scala @@ -17,6 +17,11 @@ class SimpleParent case class Simple() extends SimpleParent case object SimpleSibling extends SimpleParent +// value classes +final class ValueClass1(val value: Int) extends AnyVal +final class ValueClass2[T](val value: T) extends AnyVal +final case class ValueClass3(val value: Int) extends AnyVal + /* It's not possible to run partest without -deprecation. * Since detecting the warnings requires a neg test with * -Xfatal-warnings, and deprecation terminates the compile, @@ -63,6 +68,19 @@ object Test { println(new Simple() == SimpleSibling) // like Some(1) == None, but needn't be final case + println(new ValueClass1(5) == new ValueClass1(5)) // ok + println(new ValueClass1(5) == 5) // bad + println(new ValueClass1(5) == (5: Any)) // ok, have to let it through + println(5 == new ValueClass1(5)) // bad + println((5: Any) == new ValueClass1(5) == (5: Any)) // ok + + println(new ValueClass2("abc") == new ValueClass2("abc")) // ok + println(new ValueClass2("abc") == "abc") // bad + println(new ValueClass2(5) == new ValueClass1(5)) // bad - different value classes + println(ValueClass3(5) == new ValueClass3(5)) // ok + println(ValueClass3(5) == new ValueClass2(5)) // bad + println(ValueClass3(5) == 5) // bad + /* val mine = new MyThing val some = new SomeThing -- cgit v1.2.3 From 83c059dd776408a921473b85685192cf807c3dbd Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 30 Apr 2013 14:56:47 +0200 Subject: use relative symlink in distpack To simplify building a release on jenkins, we run distpack-opt in one job, store the `dists/` directory in a tar ball, archive that artifact and copy it to the downstream jobs that package on windows and unix. To make the tarball portable between machines, it must not use absolute symlinks. --- src/build/pack.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/build/pack.xml b/src/build/pack.xml index 381d3f1931..20c4034107 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -112,8 +112,10 @@ MAIN DISTRIBUTION PACKAGING + -- cgit v1.2.3