From 1cc06bb6976e2d211b6219cfa7cdfbaab193791e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 1 May 2007 14:01:18 +0000 Subject: more additions to checknull. --- src/compiler/scala/tools/nsc/Settings.scala | 1 + src/compiler/scala/tools/nsc/ast/TreeGen.scala | 11 +++++++--- .../scala/tools/nsc/typechecker/Typers.scala | 25 ++++++++++++++-------- test/files/neg/bug997.check | 13 +++++++++++ test/files/neg/bug997.scala | 15 +++++++++++++ 5 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 test/files/neg/bug997.check create mode 100755 test/files/neg/bug997.scala diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index b984a97469..848fc8a8f1 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -86,6 +86,7 @@ class Settings(error: String => unit) { val outdir = StringSetting ("-d", "directory", "Specify where to place generated class files", ".") val encoding = new StringSetting ("-encoding", "encoding", "Specify character encoding used by source files", encodingDefault) { override def hiddenToIDE = false } val target = ChoiceSetting ("-target", "Specify which backend to use", List("jvm-1.5", "jvm-1.4", "msil", "cldc"), "jvm-1.4") + val checknull = BooleanSetting("-checknull", "Emit warning on selection of nullable reference") val migrate = BooleanSetting("-migrate", "Assist in migrating from Scala version 1.0") val assemname = StringSetting ("-o", "file", "Name of the output assembly (only relevant with -target:msil)", "").dependsOn(target, "msil") val assemrefs = StringSetting ("-r", "path", "List of assemblies referenced by the program (only relevant with -target:msil)", ".").dependsOn(target, "msil") diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 2baea3f4e2..5a2ffbf875 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -156,10 +156,15 @@ abstract class TreeGen { def mkAsInstanceOf(value: Tree, tpe: Type): Tree = mkAsInstanceOf(value, tpe, global.phase.erasedTypes) - def mkCheckInit(tree: Tree): Tree = - if (tree.hasSymbol && tree.symbol.tpe <:< NotNullClass.tpe && !tree.symbol.tpe.isNotNull) + def mkCheckInit(tree: Tree): Tree = { + var tpe = tree.tpe + if (tpe == null && tree.hasSymbol) tpe = tree.symbol.tpe + if (!global.phase.erasedTypes && settings.checknull.value && + tpe <:< NotNullClass.tpe && !tpe.isNotNull) mkRuntimeCall(nme.checkInitialized, List(tree)) - else tree + else + tree + } /** Builds a list with given head and tail. */ def mkNewCons(head: Tree, tail: Tree): Tree = diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6ef190be69..8044f2ce5d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2011,15 +2011,19 @@ trait Typers requires Analyzer { case fun1: Tree => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 if (util.Statistics.enabled) appcnt = appcnt + 1 - if (phase.id <= currentRun.typerPhase.id && - fun2.isInstanceOf[Select] && - !fun2.tpe.isInstanceOf[ImplicitMethodType] && - ((fun2.symbol eq null) || !fun2.symbol.isConstructor) && - (mode & (EXPRmode | SNDTRYmode)) == EXPRmode) { - tryTypedApply(fun2, args) - } else { - doTypedApply(tree, fun2, args, mode, pt) - } + val res = + if (phase.id <= currentRun.typerPhase.id && + fun2.isInstanceOf[Select] && + !fun2.tpe.isInstanceOf[ImplicitMethodType] && + ((fun2.symbol eq null) || !fun2.symbol.isConstructor) && + (mode & (EXPRmode | SNDTRYmode)) == EXPRmode) { + tryTypedApply(fun2, args) + } else { + doTypedApply(tree, fun2, args, mode, pt) + } + if (fun2.symbol == Array_apply) typed { atPos(tree.pos) { gen.mkCheckInit(res) } } + else res + case ex: TypeError => fun match { case Select(qual, name) @@ -2180,6 +2184,9 @@ trait Typers requires Analyzer { } val result = stabilize(checkAccessible(tree1, sym, qual.tpe, qual), qual.tpe, mode, pt) if (sym.isCaseFactory && !phase.erasedTypes) checkStable(qual) + if (!global.phase.erasedTypes && settings.checknull.value && + !(qual.tpe <:< NotNullClass.tpe) && !qual.tpe.isNotNull) + unit.warning(tree.pos, "potential null pointer dereference") result } } diff --git a/test/files/neg/bug997.check b/test/files/neg/bug997.check new file mode 100644 index 0000000000..ba6dfd94bb --- /dev/null +++ b/test/files/neg/bug997.check @@ -0,0 +1,13 @@ +bug997.scala:7: error: wrong number of arguments for object Foo of type Foo.type +"x" match { case Foo(a) => Console.println(a) } + ^ +bug997.scala:7: error: not found: value a +"x" match { case Foo(a) => Console.println(a) } + ^ +bug997.scala:13: error: wrong number of arguments for object Foo of type Foo.type +"x" match { case Foo(a, b, c) => Console.println((a,b,c)) } + ^ +bug997.scala:13: error: not found: value a +"x" match { case Foo(a, b, c) => Console.println((a,b,c)) } + ^ +four errors found diff --git a/test/files/neg/bug997.scala b/test/files/neg/bug997.scala new file mode 100755 index 0000000000..b6897e62df --- /dev/null +++ b/test/files/neg/bug997.scala @@ -0,0 +1,15 @@ +// An extractor with 2 results +object Foo { def unapply(x : String) = Some(Pair(x, x)) } + +object Test extends Application { + +// Prints 'x'; ought not to compile (or maybe a should be the Pair?). +"x" match { case Foo(a) => Console.println(a) } + +// Prints '(x,x)' as expected. +"x" match { case Foo(a, b) => Console.println((a,b)) } + +// Gives confusing error 'not found: value c'. +"x" match { case Foo(a, b, c) => Console.println((a,b,c)) } + +} -- cgit v1.2.3