diff options
63 files changed, 1064 insertions, 316 deletions
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 087fa07b37..d8a91f3ae8 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -173,6 +173,15 @@ filter { { matchName="scala.reflect.runtime.SymbolLoaders.isInvalidClassName" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.JavaMirrors#JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$followStatic" + problemName=MissingMethodProblem + }, + { + # only accessible from util.parsing.combinator package + matchName="scala.util.parsing.combinator.SubSequence" + problemName=MissingClassProblem } ] } diff --git a/build.number b/build.number index 7c027e7797..de2c2fb824 100644 --- a/build.number +++ b/build.number @@ -1,7 +1,7 @@ #Tue Sep 11 19:21:09 CEST 2007 version.major=2 version.minor=10 -version.patch=4 +version.patch=5 # This is the -N part of a version. if it's 0, it's dropped from maven versions. version.bnum=0 diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/runtime/Typers.scala index a51bee0fe8..f62c5e90ff 100644 --- a/src/compiler/scala/reflect/macros/runtime/Typers.scala +++ b/src/compiler/scala/reflect/macros/runtime/Typers.scala @@ -14,15 +14,11 @@ trait Typers { def typeCheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { macroLogVerbose("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) val context = callsiteTyper.context - val wrapper1 = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) - val wrapper2 = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) - def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - // if you get a "silent mode is not available past typer" here - // don't rush to change the typecheck not to use the silent method when the silent parameter is false - // typechecking uses silent anyways (e.g. in typedSelect), so you'll only waste your time - // I'd advise fixing the root cause: finding why the context is not set to report errors - // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) - wrapper(callsiteTyper.silent(_.typed(tree, universe.analyzer.EXPRmode, pt), reportAmbiguousErrors = false) match { + val withImplicitFlag = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) + val withMacroFlag = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) + def withContext(tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) + def typecheckInternal(tree: Tree) = callsiteTyper.silent(_.typed(tree, universe.analyzer.EXPRmode, pt), reportAmbiguousErrors = false) + universe.wrappingIntoTerm(tree)(wrappedTree => withContext(typecheckInternal(wrappedTree) match { case universe.analyzer.SilentResultValue(result) => macroLogVerbose(result) result @@ -30,7 +26,7 @@ trait Typers { macroLogVerbose(error.err.errMsg) if (!silent) throw new TypecheckException(error.err.errPos, error.err.errMsg) universe.EmptyTree - }) + })) } def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala index cbbb4c8ba8..1005cd1ccf 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -21,6 +21,11 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo { import definitions.ThrowableClass + // TODO these overrides, and the slow trickle of bugs that they solve (e.g. SI-8479), + // suggest that we should pursue an alternative design in which the DocDef nodes + // are eliminated from the tree before typer, and instead are modelled as tree + // attachments. + /** Is tree legal as a member definition of an interface? */ override def isInterfaceMember(tree: Tree): Boolean = tree match { @@ -28,6 +33,11 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo { case _ => super.isInterfaceMember(tree) } + override def isConstructorWithDefault(t: Tree) = t match { + case DocDef(_, definition) => isConstructorWithDefault(definition) + case _ => super.isConstructorWithDefault(t) + } + /** Is tree a pure (i.e. non-side-effecting) definition? */ override def isPureDef(tree: Tree): Boolean = tree match { diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index dbfaa2c531..56fc4d7594 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -179,6 +179,7 @@ trait ScalaSettings extends AbsScalaSettings val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() + val YfundepMaterialization = BooleanSetting("-Yfundep-materialization", "Turn on the 2.11 behavior of macro expansion being able to influence type inference in implicit searches") def stop = stopAfter diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 2955986a7e..6991cfa37b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -436,9 +436,13 @@ abstract class ClassfileParser { // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects // that are not in their correct place (see bug for details) - if (!settings.isScaladoc) - warning(s"Class $name not found - continuing with a stub.") - return NoSymbol.newClass(name.toTypeName) + + // TODO More consistency with use of stub symbols in `Unpickler` + // - better owner than `NoSymbol` + // - remove eager warning + val msg = s"Class $name not found - continuing with a stub." + if (!settings.isScaladoc) warning(msg) + return NoSymbol.newStubSymbol(name.toTypeName, msg) } val completer = new global.loaders.ClassfileLoader(file) var owner: Symbol = rootMirror.RootClass diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index d6ec5f2cb0..c0844ec8fc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -713,6 +713,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { var expectedTpe = expandee.tpe if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType + if (settings.YfundepMaterialization.value) { + // approximation is necessary for whitebox macros to guide type inference + // read more in the comments for onDelayed below + val undetparams = expectedTpe collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } + expectedTpe = deriveTypeWithWildcards(undetparams)(expectedTpe) + } + // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc val expanded0 = duplicateAndKeepPositions(expanded) val expanded1 = typecheck("macro def return type", expanded0, expectedTpe) @@ -766,9 +773,24 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype, // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want. + // + // =========== THE SOLUTION =========== + // + // To give materializers a chance to say their word before vanilla inference kicks in, + // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo) + // and then trigger macro expansion with the undetermined type parameters still there. + // Thanks to that the materializer can take a look at what's going on and react accordingly. + // + // NOTE: This functionality is only available under the -Xfundep-materialization flag in Scala 2.10, + // but is enabled by default in Scala 2.11. val shouldInstantiate = typer.context.undetparams.nonEmpty && !inPolyMode(mode) - if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) - else delayed + if (shouldInstantiate) { + if (settings.YfundepMaterialization.value) { + forced += delayed + typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) + macroExpand(typer, delayed, mode, pt) + } else typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) + } else delayed case Fallback(fallback) => typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) case Other(result) => @@ -886,10 +908,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * 2) undetparams (sym.isTypeParameter && !sym.isSkolem) */ var hasPendingMacroExpansions = false + private val forced = perRunCaches.newWeakSet[Tree] private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]] private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): scala.collection.mutable.Set[Int] = - delayed.get(expandee).getOrElse { + // !settings.YfundepMaterialization.value implies forced.isEmpty + if (forced(expandee)) scala.collection.mutable.Set[Int]() + else delayed.getOrElse(expandee, { val calculated = scala.collection.mutable.Set[Symbol]() expandee foreach (sub => { def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym @@ -898,7 +923,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { }) macroLogVerbose("calculateUndetparams: %s".format(calculated)) calculated map (_.id) - } + }) private val undetparams = perRunCaches.newSet[Int] def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = { undetparams ++= newUndets map (_.id) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index bb938074cb..45da6d80d6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -663,10 +663,7 @@ trait Namers extends MethodSynthesis { val m = ensureCompanionObject(tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) } - val hasDefault = impl.body exists { - case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, _) => mexists(vparamss)(_.mods.hasDefault) - case _ => false - } + val hasDefault = impl.body exists treeInfo.isConstructorWithDefault if (hasDefault) { val m = ensureCompanionObject(tree) m.updateAttachment(new ConstructorDefaultsAttachment(tree, null)) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 70f2f41ec7..307e17d97a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -175,9 +175,9 @@ trait NamesDefaults { self: Analyzer => // setSymbol below is important because the 'selected' function might be overloaded. by // assigning the correct method symbol, typedSelect will just assign the type. the reason // to still call 'typed' is to correctly infer singleton types, SI-5259. - val selectPos = - if(qual.pos.isRange && baseFun.pos.isRange) qual.pos.union(baseFun.pos).withStart(Math.min(qual.pos.end, baseFun.pos.end)) - else baseFun.pos + val selectPos = + if(qual.pos.isRange && baseFun1.pos.isRange) qual.pos.union(baseFun1.pos).withStart(Math.min(qual.pos.end, baseFun1.pos.end)) + else baseFun1.pos val f = blockTyper.typedOperator(Select(newQual, selected).setSymbol(baseFun1.symbol).setPos(selectPos)) if (funTargs.isEmpty) f else TypeApply(f, funTargs).setType(baseFun.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e09a509839..8153766784 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4934,7 +4934,7 @@ trait Typers extends Modes with Adaptations with Tags { if (tree.isInstanceOf[PostfixSelect]) checkFeature(tree.pos, PostfixOpsFeature, name.decode) - if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember) + if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember && !tree1.symbol.isMacro) checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString) if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name) diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 8803980dac..650598f9db 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -5,9 +5,10 @@ import scala.tools.nsc.reporters._ import scala.tools.nsc.CompilerCommand import scala.tools.nsc.Global import scala.tools.nsc.typechecker.Modes -import scala.tools.nsc.io.VirtualDirectory +import scala.tools.nsc.io.{AbstractFile, VirtualDirectory} import scala.tools.nsc.interpreter.AbstractFileClassLoader import scala.tools.nsc.util.FreshNameCreator +import scala.tools.nsc.util.CommandLineParser import scala.tools.nsc.ast.parser.Tokens.EOF import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} @@ -17,6 +18,7 @@ import scala.reflect.NameTransformer import scala.reflect.api.JavaUniverse import scala.reflect.io.NoAbstractFile import scala.tools.nsc.interactive.RangePositions +import scala.reflect.internal.FatalError abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => @@ -32,6 +34,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, factorySelf.mirror.classLoader) lazy val mirror: u.Mirror = u.runtimeMirror(classLoader) + lazy val arguments = CommandLineParser.tokenize(options) + lazy val virtualDirectory = + arguments.iterator.sliding(2).collectFirst{ case Seq("-d", dir) => dir } match { + case Some(outDir) => AbstractFile.getDirectory(outDir) + case None => new VirtualDirectory("(memory)", None) + } + class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter: Reporter) extends ReflectGlobal(settings, reporter, toolBoxSelf.classLoader) { import definitions._ @@ -50,7 +59,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } // should be called after every use of ToolBoxGlobal in order to prevent leaks - // there's the `withCleanupCaches` method defined below, which provides a convenient interface for that def cleanupCaches(): Unit = { perRunCaches.clearAll() undoLog.clear() @@ -59,11 +67,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lastSeenContext = null } - def withCleanupCaches[T](body: => T): T = - try body - finally cleanupCaches() - - def verify(expr: Tree): Unit = { + def verify(expr: Tree): Tree = { // Previously toolboxes used to typecheck their inputs before compiling. // Actually, the initial demo by Martin first typechecked the reified tree, // then ran it, which typechecked it again, and only then launched the @@ -84,14 +88,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => msg += "if you have troubles tracking free type variables, consider using -Xlog-free-types" throw ToolBoxError(msg) } - } - - def wrapIntoTerm(tree: Tree): Tree = - if (!tree.isTerm) Block(List(tree), Literal(Constant(()))) else tree - def unwrapFromTerm(tree: Tree): Tree = tree match { - case Block(List(tree), Literal(Constant(()))) => tree - case tree => tree + expr } def extractFreeTerms(expr0: Tree, wrapFreeTermRefs: Boolean): (Tree, scala.collection.mutable.LinkedHashMap[FreeTermSymbol, TermName]) = { @@ -121,50 +119,51 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } def transformDuringTyper(expr0: Tree, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { - verify(expr0) - - // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars - var (expr, freeTerms) = extractFreeTerms(expr0, wrapFreeTermRefs = false) - val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList - expr = Block(dummies, wrapIntoTerm(expr)) - - // [Eugene] how can we implement that? - // !!! Why is this is in the empty package? If it's only to make - // it inaccessible then please put it somewhere designed for that - // rather than polluting the empty package with synthetics. - val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) - build.setTypeSignature(ownerClass, ClassInfoType(List(ObjectClass.tpe), newScope, ownerClass)) - val owner = ownerClass.newLocalDummy(expr.pos) - var currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr, owner)) - val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) - val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) - def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - - val run = new Run - run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works - phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled - currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions - reporter.reset() - - val expr1 = wrapper(transform(currentTyper, expr)) - var (dummies1, unwrapped) = expr1 match { - case Block(dummies, unwrapped) => (dummies, unwrapped) - case unwrapped => (Nil, unwrapped) - } - var invertedIndex = freeTerms map (_.swap) - // todo. also fixup singleton types - unwrapped = new Transformer { - override def transform(tree: Tree): Tree = - tree match { - case Ident(name) if invertedIndex contains name => - Ident(invertedIndex(name)) setType tree.tpe - case _ => - super.transform(tree) - } - }.transform(unwrapped) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name)))).traverse(unwrapped) - unwrapped = if (expr0.isTerm) unwrapped else unwrapFromTerm(unwrapped) - unwrapped + wrappingIntoTerm(verify(expr0))(expr1 => { + // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars + val exprAndFreeTerms = extractFreeTerms(expr1, wrapFreeTermRefs = false) + var expr2 = exprAndFreeTerms._1 + val freeTerms = exprAndFreeTerms._2 + val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList + expr2 = Block(dummies, expr2) + + // [Eugene] how can we implement that? + // !!! Why is this is in the empty package? If it's only to make + // it inaccessible then please put it somewhere designed for that + // rather than polluting the empty package with synthetics. + val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) + build.setTypeSignature(ownerClass, ClassInfoType(List(ObjectClass.tpe), newScope, ownerClass)) + val owner = ownerClass.newLocalDummy(expr2.pos) + var currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr2, owner)) + val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) + val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) + def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) + + val run = new Run + run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works + phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + reporter.reset() + + val expr3 = wrapper(transform(currentTyper, expr2)) + var (dummies1, result) = expr3 match { + case Block(dummies, result) => (dummies, result) + case result => (Nil, result) + } + var invertedIndex = freeTerms map (_.swap) + // todo. also fixup singleton types + result = new Transformer { + override def transform(tree: Tree): Tree = + tree match { + case Ident(name) if invertedIndex contains name => + Ident(invertedIndex(name)) setType tree.tpe + case _ => + super.transform(tree) + } + }.transform(result) + new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name)))).traverse(result) + result + }) } def typeCheck(expr: Tree, pt: Type, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = @@ -317,42 +316,51 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } } - // todo. is not going to work with quoted arguments with embedded whitespaces - lazy val arguments = options.split(" ") + trait CompilerApi { + val compiler: ToolBoxGlobal + val importer: compiler.Importer { val from: u.type } + val exporter: u.Importer { val from: compiler.type } + } - lazy val virtualDirectory = - (arguments zip arguments.tail).collect{ case ("-d", dir) => dir }.lastOption match { - case Some(outDir) => scala.tools.nsc.io.AbstractFile.getDirectory(outDir) - case None => new VirtualDirectory("(memory)", None) + object withCompilerApi { + private object api extends CompilerApi { + lazy val compiler: ToolBoxGlobal = { + try { + val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR) + val command = new CompilerCommand(arguments.toList, errorFn) + val settings = command.settings + settings.outputDirs setSingleOutput virtualDirectory + val reporter = frontEndToReporter(frontEnd, command.settings) + val instance = + if (settings.Yrangepos.value) new ToolBoxGlobal(settings, reporter) with RangePositions + else new ToolBoxGlobal(settings, reporter) + if (frontEnd.hasErrors) { + var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL + msg += frontEnd.infos map (_.msg) mkString EOL + throw ToolBoxError(msg) + } + instance + } catch { + case ex: Throwable => + var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString) + throw ToolBoxError(msg, ex) + } + } + + lazy val importer = compiler.mkImporter(u) + lazy val exporter = importer.reverse } - lazy val compiler: ToolBoxGlobal = { - try { - val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR) - val command = new CompilerCommand(arguments.toList, errorFn) - val settings = command.settings - settings.outputDirs setSingleOutput virtualDirectory - val reporter = frontEndToReporter(frontEnd, command.settings) - val instance = - if (settings.Yrangepos.value) new ToolBoxGlobal(settings, reporter) with RangePositions - else new ToolBoxGlobal(settings, reporter) - if (frontEnd.hasErrors) { - var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL - msg += frontEnd.infos map (_.msg) mkString EOL - throw ToolBoxError(msg) - } - instance - } catch { - case ex: Throwable => - var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString) - throw ToolBoxError(msg, ex) + def apply[T](f: CompilerApi => T): T = { + object FatalError { def unapply(ex: Throwable) = ex match { case _: FatalError | _: AssertionError => Some(ex); case _ => None } } + try f(api) catch { case FatalError(ex) => throw ToolBoxError(s"fatal compiler error", ex) } + finally api.compiler.cleanupCaches() } } - lazy val importer = compiler.mkImporter(u) - lazy val exporter = importer.reverse + def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ - def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = compiler.withCleanupCaches { if (compiler.settings.verbose.value) println("importing "+tree+", expectedType = "+expectedType) var ctree: compiler.Tree = importer.importTree(tree) var cexpectedType: compiler.Type = importer.importType(expectedType) @@ -372,7 +380,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => inferImplicit(tree, viewTpe, isView = true, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos) } - private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = compiler.withCleanupCaches { + private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ + if (compiler.settings.verbose.value) println("importing "+pt, ", tree = "+tree+", pos = "+pos) var ctree: compiler.Tree = importer.importTree(tree) var cpt: compiler.Type = importer.importType(pt) @@ -384,31 +394,38 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => uitree } - def resetAllAttrs(tree: u.Tree): u.Tree = { + def resetAllAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ val ctree: compiler.Tree = importer.importTree(tree) val ttree: compiler.Tree = compiler.resetAllAttrs(ctree) val uttree = exporter.importTree(ttree) uttree } - def resetLocalAttrs(tree: u.Tree): u.Tree = { + def resetLocalAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ val ctree: compiler.Tree = importer.importTree(tree) val ttree: compiler.Tree = compiler.resetLocalAttrs(ctree) val uttree = exporter.importTree(ttree) uttree } - def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = + def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = withCompilerApi { compilerApi => + import compilerApi._ compiler.showAttributed(importer.importTree(tree), printTypes, printIds, printKinds) + } - def parse(code: String): u.Tree = { + def parse(code: String): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ if (compiler.settings.verbose.value) println("parsing "+code) val ctree: compiler.Tree = compiler.parse(code) val utree = exporter.importTree(ctree) utree } - def compile(tree: u.Tree): () => Any = { + def compile(tree: u.Tree): () => Any = withCompilerApi { compilerApi => + import compilerApi._ + if (compiler.settings.verbose.value) println("importing "+tree) val ctree: compiler.Tree = importer.importTree(tree) diff --git a/src/library/scala/runtime/SeqCharSequence.scala b/src/library/scala/runtime/SeqCharSequence.scala index d2084a6598..a7765994ec 100644 --- a/src/library/scala/runtime/SeqCharSequence.scala +++ b/src/library/scala/runtime/SeqCharSequence.scala @@ -41,5 +41,10 @@ final class ArrayCharSequence(val xs: Array[Char], start: Int, end: Int) extends new ArrayCharSequence(xs, start1, start1 + newlen) } } - override def toString = xs drop start take length mkString "" + override def toString = { + val start = math.max(this.start, 0) + val end = math.min(xs.length, start + length) + + if (start >= end) "" else new String(xs, start, end - start) + } } diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 716d746552..8d82e08d7f 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -1,12 +1,11 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2007-2013, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2007-2014, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ - /** * This package is concerned with regular expression (regex) matching against strings, * with the main goal of pulling out information from those matches, or replacing @@ -33,97 +32,132 @@ package scala.util.matching import scala.collection.AbstractIterator import java.util.regex.{ Pattern, Matcher } -/** This class provides methods for creating and using regular expressions. - * It is based on the regular expressions of the JDK since 1.4. +/** A regular expression is used to determine whether a string matches a pattern + * and, if it does, to extract or transform the parts that match. * - * Its main goal is to extract strings that match a pattern, or the subgroups - * that make it up. For that reason, it is usually used with for comprehensions - * and matching (see methods for examples). + * This class delegates to the [[java.util.regex]] package of the Java Platform. + * See the documentation for [[java.util.regex.Pattern]] for details about + * the regular expression syntax for pattern strings. * - * A Regex is created from a [[java.lang.String]] representation of the - * regular expression pattern^1^. That pattern is compiled - * during construction, so frequently used patterns should be declared outside - * loops if performance is of concern. Possibly, they might be declared on a - * companion object, so that they need only to be initialized once. + * An instance of `Regex` represents a compiled regular expression pattern. + * Since compilation is expensive, frequently used `Regex`es should be constructed + * once, outside of loops and perhaps in a companion object. * - * The canonical way of creating regex patterns is by using the method `r`, provided - * on [[java.lang.String]] through an implicit conversion into - * [[scala.collection.immutable.WrappedString]]. Using triple quotes to write these - * strings avoids having to quote the backslash character (`\`). + * The canonical way to create a `Regex` is by using the method `r`, provided + * implicitly for strings: * - * Using the constructor directly, on the other hand, makes - * it possible to declare names for subgroups in the pattern. + * {{{ + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * }}} * - * For example, both declarations below generate the same regex, but the second - * one associate names with the subgroups. + * Since escapes are not processed in multi-line string literals, using triple quotes + * avoids having to escape the backslash character, so that `"\\d"` can be written `"""\d"""`. + * + * To extract the capturing groups when a `Regex` is matched, use it as + * an extractor in a pattern match: * * {{{ - * val dateP1 = """(\d\d\d\d)-(\d\d)-(\d\d)""".r - * val dateP2 = new scala.util.matching.Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * "2004-01-20" match { + * case date(year, month, day) => s"$year was a good year for PLs." + * } * }}} * - * There are two ways of using a `Regex` to find a pattern: calling methods on - * Regex, such as `findFirstIn` or `findAllIn`, or using it as an extractor in a - * pattern match. + * To check only whether the `Regex` matches, ignoring any groups, + * use a sequence wildcard: * - * Note, however, that when Regex is used as an extractor in a pattern match, it - * only succeeds if the whole text can be matched. For this reason, one usually - * calls a method to find the matching substrings, and then use it as an extractor - * to break match into subgroups. + * {{{ + * "2004-01-20" match { + * case date(_*) => "It's a date!" + * } + * }}} * - * As an example, the above patterns can be used like this: + * That works because a `Regex` extractor produces a sequence of strings. + * Extracting only the year from a date could also be expressed with + * a sequence wildcard: * * {{{ - * val dateP1(year, month, day) = "2011-07-15" + * "2004-01-20" match { + * case date(year, _*) => s"$year was a good year for PLs." + * } + * }}} * - * // val dateP1(year, month, day) = "Date 2011-07-15" // throws an exception at runtime + * In a pattern match, `Regex` normally matches the entire input. + * However, an unanchored `Regex` finds the pattern anywhere + * in the input. * - * val copyright: String = dateP1 findFirstIn "Date of this document: 2011-07-15" match { - * case Some(dateP1(year, month, day)) => "Copyright "+year - * case None => "No copyright" + * {{{ + * val embeddedDate = date.unanchored + * "Date: 2004-01-20 17:25:18 GMT (10 years, 28 weeks, 5 days, 17 hours and 51 minutes ago)" match { + * case embeddedDate("2004", "01", "20") => "A Scala is born." * } + * }}} * - * val copyright: Option[String] = for { - * dateP1(year, month, day) <- dateP1 findFirstIn "Last modified 2011-07-15" - * } yield year - - * def getYears(text: String): Iterator[String] = for (dateP1(year, _, _) <- dateP1 findAllIn text) yield year - * def getFirstDay(text: String): Option[String] = for (m <- dateP2 findFirstMatchIn text) yield m group "day" + * To find or replace matches of the pattern, use the various find and replace methods. + * There is a flavor of each method that produces matched strings and + * another that produces `Match` objects. + * + * For example, pattern matching with an unanchored `Regex`, as in the previous example, + * is the same as using `findFirstMatchIn`, except that the findFirst methods return an `Option`, + * or `None` for no match: + * + * {{{ + * val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15" + * val firstDate = date findFirstIn dates getOrElse "No date found." + * val firstYear = for (m <- date findFirstMatchIn dates) yield m group 1 * }}} * - * Regex does not provide a method that returns a [[scala.Boolean]]. One can - * use [[java.lang.String]] `matches` method, or, if `Regex` is preferred, - * either ignore the return value or test the `Option` for emptyness. For example: + * To find all matches: * * {{{ - * def hasDate(text: String): Boolean = (dateP1 findFirstIn text).nonEmpty - * def printLinesWithDates(lines: Traversable[String]) { - * lines foreach { line => - * dateP1 findFirstIn line foreach { _ => println(line) } - * } - * } + * val allYears = for (m <- date findAllMatchIn dates) yield m group 1 + * }}} + * + * But `findAllIn` returns a special iterator of strings that can be queried for the `MatchData` + * of the last match: + * + * {{{ + * val mi = date findAllIn dates + * val oldies = mi filter (_ => (mi group 1).toInt < 1960) map (s => s"$s: An oldie but goodie.") + * }}} + * + * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) + * + * {{{ + * val num = """(\d+)""".r + * val all = (num findAllIn "123").toList // List("123"), not List("123", "23", "3") + * }}} + * + * Text replacement can be performed unconditionally or as a function of the current match: + * + * {{{ + * val redacted = date replaceAllIn (dates, "XXXX-XX-XX") + * val yearsOnly = date replaceAllIn (dates, m => m group 1) + * val months = (0 to 11) map { i => val c = Calendar.getInstance; c.set(2014, i, 1); f"$c%tb" } + * val reformatted = date replaceAllIn (dates, _ match { case Groups(y,m,d) => f"${months(m.toInt - 1)} $d, $y" }) + * }}} + * + * The `Groups` extractor is used to extract groups from a `Match` without reapplying the `Regex`. + * In the expression for `reformatted`, each `date` match is computed once. But it is possible to apply a + * `Regex` to a `Match` resulting from a different pattern: + * + * {{{ + * val docSpree = """2011(?:-\d{2}){2}""".r + * val docView = date replaceAllIn (dates, _ match { + * case docSpree() => "Historic doc spree!" + * case _ => "Something else happened" + * }) * }}} * - * There are also methods that can be used to replace the patterns - * on a text. The substitutions can be simple replacements, or more - * complex functions. For example: + * If group names are supplied to the `Regex` constructor, they can be used this way: * * {{{ - * val months = Map( 1 -> "Jan", 2 -> "Feb", 3 -> "Mar", - * 4 -> "Apr", 5 -> "May", 6 -> "Jun", - * 7 -> "Jul", 8 -> "Aug", 9 -> "Sep", - * 10 -> "Oct", 11 -> "Nov", 12 -> "Dec") - * - * import scala.util.matching.Regex.Match - * def reformatDate(text: String) = dateP2 replaceAllIn ( text, (m: Match) => - * "%s %s, %s" format (months(m group "month" toInt), m group "day", m group "year") - * ) + * val namedDate = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year" * }}} * - * You can use special pattern syntax constructs like `(?idmsux-idmsux)`¹ to switch - * various regex compilation options like `CASE_INSENSITIVE` or `UNICODE_CASE`. + * This constructor does not support options as flags, which must be + * supplied as inline flags in the pattern string: `(?idmsux-idmsux)`. * - * @note ¹ A detailed description is available in [[java.util.regex.Pattern]]. * @see [[java.util.regex.Pattern]] * * @author Thibaud Hottelier @@ -139,9 +173,8 @@ import java.util.regex.{ Pattern, Matcher } * interpreted as a reference to a group in the matched pattern, with numbers * 1 through 9 corresponding to the first nine groups, and 0 standing for the * whole match. Any other character is an error. The backslash (`\`) character - * will be interpreted as an escape character, and can be used to escape the - * dollar sign. One can use [[scala.util.matching.Regex]]'s `quoteReplacement` - * to automatically escape these characters. + * will be interpreted as an escape character and can be used to escape the + * dollar sign. Use `Regex.quoteReplacement` to escape these characters. */ @SerialVersionUID(-2094783597747625537L) class Regex(regex: String, groupNames: String*) extends Serializable { @@ -152,30 +185,60 @@ class Regex(regex: String, groupNames: String*) extends Serializable { /** The compiled pattern */ val pattern = Pattern.compile(regex) - /** Tries to match target (whole match) and returns the matching subgroups. - * if the pattern has no subgroups, then it returns an empty list on a - * successful match. + /** Tries to match either a [[java.lang.CharSequence]] or the matched + * input of a previous `Match`. * - * Note, however, that if some subgroup has not been matched, a `null` will - * be returned for that subgroup. + * If the match of a `CharSequence` succeeds, the result is a list of the capturing + * groups (with a `null` element if a group did not match any input). + * If the pattern specifies no groups, then the result will be an empty list + * on a successful match. + * + * This method attempts to match the entire input by default; to find the next + * matching subsequence, use an unanchored `Regex`. * * For example: * * {{{ * val p1 = "ab*c".r - * val p2 = "a(b*)c".r - * * val p1Matches = "abbbc" match { - * case p1() => true + * case p1() => true // no groups * case _ => false * } - * + * val p2 = "a(b*)c".r + * val p2Matches = "abbbc" match { + * case p2(_*) => true // any groups + * case _ => false + * } * val numberOfB = "abbbc" match { - * case p2(b) => Some(b.length) + * case p2(b) => Some(b.length) // one group * case _ => None * } + * val p3 = "b*".r.unanchored + * val p3Matches = "abbbc" match { + * case p3() => true // find the b's + * case _ => false + * } + * val p4 = "a(b*)(c+)".r + * val p4Matches = "abbbcc" match { + * case p4(_*) => true // multiple groups + * case _ => false + * } + * val allGroups = "abbbcc" match { + * case p4(all @ _*) => all mkString "/" // "bbb/cc" + * case _ => "" + * } + * val cGroup = "abbbcc" match { + * case p4(_, c) => c + * case _ => "" + * } * }}} * + * When matching a [[scala.util.matching.Regex.Match]], + * a previously failed match results in None. + * + * Otherwise, this `Regex` is applied to the previously matched input, + * and the result of that match is used. + * * @param target The string to match * @return The matches */ @@ -184,102 +247,127 @@ class Regex(regex: String, groupNames: String*) extends Serializable { val m = pattern matcher s if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) else None - case m: Match => unapplySeq(m.matched) - case _ => None + case m: Match => unapplySeq(m.matched) + case _ => None } + + // @see UnanchoredRegex protected def runMatcher(m: Matcher) = m.matches() - /** Return all matches of this regexp in given character sequence as a [[scala.util.matching.Regex.MatchIterator]], + /** Return all non-overlapping matches of this `Regex` in the given character + * sequence as a [[scala.util.matching.Regex.MatchIterator]], * which is a special [[scala.collection.Iterator]] that returns the - * matched strings, but can also be converted into a normal iterator - * that returns objects of type [[scala.util.matching.Regex.Match]] - * that can be queried for data such as the text that precedes the - * match, subgroups, etc. + * matched strings but can also be queried for more data about the last match, + * such as capturing groups and start position. + * + * A `MatchIterator` can also be converted into an iterator + * that returns objects of type [[scala.util.matching.Regex.Match]], + * such as is normally returned by `findAllMatchIn`. + * + * Where potential matches overlap, the first possible match is returned, + * followed by the next match that follows the input consumed by the + * first match: + * + * {{{ + * val hat = "hat[^a]+".r + * val hathaway = "hathatthattthatttt" + * val hats = (hat findAllIn hathaway).toList // List(hath, hattth) + * val pos = (hat findAllMatchIn hathaway map (_.start)).toList // List(0, 7) + * }}} + * + * To return overlapping matches, it is possible to formulate a regular expression + * with lookahead (`?=`) that does not consume the overlapping region. + * + * {{{ + * val madhatter = "(h)(?=(at[^a]+))".r + * val madhats = (madhatter findAllMatchIn hathaway map { + * case madhatter(x,y) => s"$x$y" + * }).toList // List(hath, hatth, hattth, hatttt) + * }}} + * + * Attempting to retrieve match information before performing the first match + * or after exhausting the iterator results in [[java.lang.IllegalStateException]]. + * See [[scala.util.matching.Regex.MatchIterator]] for details. * * @param source The text to match against. - * @return A [[scala.util.matching.Regex.MatchIterator]] of all matches. + * @return A [[scala.util.matching.Regex.MatchIterator]] of matched substrings. * @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}} */ - def findAllIn(source: java.lang.CharSequence) = new Regex.MatchIterator(source, this, groupNames) + def findAllIn(source: CharSequence) = new Regex.MatchIterator(source, this, groupNames) - - /** Return all matches of this regexp in given character sequence as a + /** Return all non-overlapping matches of this regexp in given character sequence as a * [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]]. * * @param source The text to match against. * @return A [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]] for all matches. * @example {{{for (words <- """\w+""".r findAllMatchIn "A simple example.") yield words.start}}} */ - def findAllMatchIn(source: java.lang.CharSequence): Iterator[Match] = { + def findAllMatchIn(source: CharSequence): Iterator[Match] = { val matchIterator = findAllIn(source) new Iterator[Match] { def hasNext = matchIterator.hasNext def next: Match = { - matchIterator.next; + matchIterator.next() new Match(matchIterator.source, matchIterator.matcher, matchIterator.groupNames).force } } } - /** Return optionally first matching string of this regexp in given character sequence, - * or None if it does not exist. + /** Return an optional first matching string of this `Regex` in the given character sequence, + * or None if there is no match. * * @param source The text to match against. * @return An [[scala.Option]] of the first matching string in the text. * @example {{{"""\w+""".r findFirstIn "A simple example." foreach println // prints "A"}}} */ - def findFirstIn(source: java.lang.CharSequence): Option[String] = { + def findFirstIn(source: CharSequence): Option[String] = { val m = pattern.matcher(source) if (m.find) Some(m.group) else None } - /** Return optionally first match of this regexp in given character sequence, + /** Return an optional first match of this `Regex` in the given character sequence, * or None if it does not exist. * - * The main difference between this method and `findFirstIn` is that the (optional) return - * type for this is [[scala.util.matching.Regex.Match]], through which more - * data can be obtained about the match, such as the strings that precede and follow it, - * or subgroups. + * If the match is successful, the [[scala.util.matching.Regex.Match]] can be queried for + * more data. * * @param source The text to match against. * @return A [[scala.Option]] of [[scala.util.matching.Regex.Match]] of the first matching string in the text. * @example {{{("""[a-z]""".r findFirstMatchIn "A simple example.") map (_.start) // returns Some(2), the index of the first match in the text}}} */ - def findFirstMatchIn(source: java.lang.CharSequence): Option[Match] = { + def findFirstMatchIn(source: CharSequence): Option[Match] = { val m = pattern.matcher(source) if (m.find) Some(new Match(source, m, groupNames)) else None } - /** Return optionally match of this regexp at the beginning of the - * given character sequence, or None if regexp matches no prefix + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix * of the character sequence. * - * The main difference from this method to `findFirstIn` is that this - * method will not return any matches that do not begin at the start - * of the text being matched against. + * Unlike `findFirstIn`, this method will only return a match at + * the beginning of the input. * * @param source The text to match against. * @return A [[scala.Option]] of the matched prefix. - * @example {{{"""[a-z]""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} + * @example {{{"""\p{Lower}""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} */ - def findPrefixOf(source: java.lang.CharSequence): Option[String] = { + def findPrefixOf(source: CharSequence): Option[String] = { val m = pattern.matcher(source) if (m.lookingAt) Some(m.group) else None } - /** Return optionally match of this regexp at the beginning of the - * given character sequence, or None if regexp matches no prefix + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix * of the character sequence. * - * The main difference from this method to `findFirstMatchIn` is that - * this method will not return any matches that do not begin at the - * start of the text being matched against. + * Unlike `findFirstMatchIn`, this method will only return a match at + * the beginning of the input. * * @param source The text to match against. * @return A [[scala.Option]] of the [[scala.util.matching.Regex.Match]] of the matched string. * @example {{{"""\w+""".r findPrefixMatchOf "A simple example." map (_.after) // returns Some(" simple example.")}}} */ - def findPrefixMatchOf(source: java.lang.CharSequence): Option[Match] = { + def findPrefixMatchOf(source: CharSequence): Option[Match] = { val m = pattern.matcher(source) if (m.lookingAt) Some(new Match(source, m, groupNames)) else None } @@ -293,7 +381,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return The resulting string * @example {{{"""\d+""".r replaceAllIn ("July 15", "<NUMBER>") // returns "July <NUMBER>"}}} */ - def replaceAllIn(target: java.lang.CharSequence, replacement: String): String = { + def replaceAllIn(target: CharSequence, replacement: String): String = { val m = pattern.matcher(target) m.replaceAll(replacement) } @@ -307,7 +395,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * import scala.util.matching.Regex * val datePattern = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") * val text = "From 2011-07-15 to 2011-07-17" - * val repl = datePattern replaceAllIn (text, m => m.group("month")+"/"+m.group("day")) + * val repl = datePattern replaceAllIn (text, m => s"${m group "month"}/${m group "day"}") * }}} * * $replacementString @@ -316,7 +404,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @param replacer The function which maps a match to another string. * @return The target string after replacements. */ - def replaceAllIn(target: java.lang.CharSequence, replacer: Match => String): String = { + def replaceAllIn(target: CharSequence, replacer: Match => String): String = { val it = new Regex.MatchIterator(target, this, groupNames).replacementData it foreach (md => it replace replacer(md)) it.replaced @@ -330,10 +418,10 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * {{{ * import scala.util.matching.Regex._ * - * val map = Map("x" -> "a var", "y" -> """some $ and \ signs""") + * val vars = Map("x" -> "a var", "y" -> """some $ and \ signs""") * val text = "A text with variables %x, %y and %z." * val varPattern = """%(\w+)""".r - * val mapper = (m: Match) => map get (m group 1) map (quoteReplacement(_)) + * val mapper = (m: Match) => vars get (m group 1) map (quoteReplacement(_)) * val repl = varPattern replaceSomeIn (text, mapper) * }}} * @@ -343,7 +431,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @param replacer The function which optionally maps a match to another string. * @return The target string after replacements. */ - def replaceSomeIn(target: java.lang.CharSequence, replacer: Match => Option[String]): String = { + def replaceSomeIn(target: CharSequence, replacer: Match => Option[String]): String = { val it = new Regex.MatchIterator(target, this, groupNames).replacementData for (matchdata <- it ; replacement <- replacer(matchdata)) it replace replacement @@ -359,7 +447,7 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @param replacement The string that will replace the match * @return The resulting string */ - def replaceFirstIn(target: java.lang.CharSequence, replacement: String): String = { + def replaceFirstIn(target: CharSequence, replacement: String): String = { val m = pattern.matcher(target) m.replaceFirst(replacement) } @@ -370,21 +458,29 @@ class Regex(regex: String, groupNames: String*) extends Serializable { * @return The array of strings computed by splitting the * input around matches of this regexp */ - def split(toSplit: java.lang.CharSequence): Array[String] = + def split(toSplit: CharSequence): Array[String] = pattern.split(toSplit) /** Create a new Regex with the same pattern, but no requirement that - * the entire String matches in extractor patterns. For instance, the strings - * shown below lead to successful matches, where they would not otherwise. + * the entire String matches in extractor patterns. + * + * Normally, matching on `date` behaves as though the pattern were + * enclosed in anchors, `"^pattern$"`. + * + * The unanchored `Regex` behaves as though those anchors were removed. + * + * Note that this method does not actually strip any matchers from the pattern. + * + * Calling `anchored` returns the original `Regex`. * * {{{ - * val dateP1 = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored * - * val dateP1(year, month, day) = "Date 2011-07-15" + * val date(year, month, day) = "Date 2011-07-15" // OK * * val copyright: String = "Date of this document: 2011-07-15" match { - * case dateP1(year, month, day) => "Copyright "+year - * case _ => "No copyright" + * case date(year, month, day) => s"Copyright $year" // OK + * case _ => "No copyright" * } * }}} * @@ -397,93 +493,96 @@ class Regex(regex: String, groupNames: String*) extends Serializable { override def toString = regex } +/** A [[Regex]] that finds the first match when used in a pattern match. + * + * @see [[Regex#unanchored]] + */ trait UnanchoredRegex extends Regex { override protected def runMatcher(m: Matcher) = m.find() override def unanchored = this } /** This object defines inner classes that describe - * regex matches and helper objects. The class hierarchy - * is as follows: - * - * {{{ - * MatchData - * / \ - * MatchIterator Match - * }}} - * + * regex matches and helper objects. */ object Regex { - /** This class provides methods to access - * the details of a match. - */ + /** This class provides methods to access the details of a match. */ trait MatchData { - /** The source from where the match originated */ - val source: java.lang.CharSequence + /** The source from which the match originated */ + val source: CharSequence - /** The names of the groups, or some empty sequence if one defined */ + /** The names of the groups, or an empty sequence if none defined */ val groupNames: Seq[String] - /** The number of subgroups in the pattern (not all of these need to match!) */ + /** The number of capturing groups in the pattern. + * (For a given successful match, some of those groups may not have matched any input.) + */ def groupCount: Int /** The index of the first matched character, or -1 if nothing was matched */ def start: Int /** The index of the first matched character in group `i`, - * or -1 if nothing was matched for that group */ + * or -1 if nothing was matched for that group. + */ def start(i: Int): Int - /** The index of the last matched character, or -1 if nothing was matched */ + /** The index following the last matched character, or -1 if nothing was matched. */ def end: Int /** The index following the last matched character in group `i`, - * or -1 if nothing was matched for that group */ + * or -1 if nothing was matched for that group. + */ def end(i: Int): Int - /** The matched string, or `null` if nothing was matched */ + /** The matched string, or `null` if nothing was matched. */ def matched: String = if (start >= 0) source.subSequence(start, end).toString else null /** The matched string in group `i`, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def group(i: Int): String = if (start(i) >= 0) source.subSequence(start(i), end(i)).toString else null - /** All matched subgroups, i.e. not including group(0) */ + /** All capturing groups, i.e., not including group(0). */ def subgroups: List[String] = (1 to groupCount).toList map group /** The char sequence before first character of match, - * or `null` if nothing was matched */ - def before: java.lang.CharSequence = + * or `null` if nothing was matched. + */ + def before: CharSequence = if (start >= 0) source.subSequence(0, start) else null /** The char sequence before first character of match in group `i`, - * or `null` if nothing was matched for that group */ - def before(i: Int): java.lang.CharSequence = + * or `null` if nothing was matched for that group. + */ + def before(i: Int): CharSequence = if (start(i) >= 0) source.subSequence(0, start(i)) else null /** Returns char sequence after last character of match, - * or `null` if nothing was matched */ - def after: java.lang.CharSequence = + * or `null` if nothing was matched. + */ + def after: CharSequence = if (end >= 0) source.subSequence(end, source.length) else null /** The char sequence after last character of match in group `i`, - * or `null` if nothing was matched for that group */ - def after(i: Int): java.lang.CharSequence = + * or `null` if nothing was matched for that group. + */ + def after(i: Int): CharSequence = if (end(i) >= 0) source.subSequence(end(i), source.length) else null private lazy val nameToIndex: Map[String, Int] = Map[String, Int]() ++ ("" :: groupNames.toList).zipWithIndex - /** Returns the group with given name + /** Returns the group with given name. * * @param id The group name * @return The requested group @@ -494,24 +593,22 @@ object Regex { case Some(index) => group(index) } - /** The matched string; equivalent to `matched.toString` */ + /** The matched string; equivalent to `matched.toString`. */ override def toString = matched - } - /** Provides information about a succesful match. - */ - class Match(val source: java.lang.CharSequence, + /** Provides information about a successful match. */ + class Match(val source: CharSequence, matcher: Matcher, val groupNames: Seq[String]) extends MatchData { - /** The index of the first matched character */ + /** The index of the first matched character. */ val start = matcher.start - /** The index following the last matched character */ + /** The index following the last matched character. */ val end = matcher.end - /** The number of subgroups */ + /** The number of subgroups. */ def groupCount = matcher.groupCount private lazy val starts: Array[Int] = @@ -519,19 +616,19 @@ object Regex { private lazy val ends: Array[Int] = ((0 to groupCount) map matcher.end).toArray - /** The index of the first matched character in group `i` */ + /** The index of the first matched character in group `i`. */ def start(i: Int) = starts(i) - /** The index following the last matched character in group `i` */ + /** The index following the last matched character in group `i`. */ def end(i: Int) = ends(i) /** The match itself with matcher-dependent lazy vals forced, - * so that match is valid even once matcher is advanced + * so that match is valid even once matcher is advanced. */ def force: this.type = { starts; ends; this } } - /** An extractor object for Matches, yielding the matched string + /** An extractor object for Matches, yielding the matched string. * * This can be used to help writing replacer functions when you * are not interested in match data. For example: @@ -546,24 +643,32 @@ object Regex { def unapply(m: Match): Some[String] = Some(m.matched) } - /** An extractor object that yields the groups in the match. Using an extractor - * rather than the original regex avoids recomputing the match. + /** An extractor object that yields the groups in the match. Using this extractor + * rather than the original `Regex` ensures that the match is not recomputed. * * {{{ * import scala.util.matching.Regex.Groups * - * val datePattern = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r * val text = "The doc spree happened on 2011-07-15." - * val day = datePattern replaceAllIn(text, _ match { case Groups(year, month, day) => month+"/"+day }) + * val day = date replaceAllIn(text, _ match { case Groups(_, month, day) => s"$month/$day" }) * }}} */ object Groups { def unapplySeq(m: Match): Option[Seq[String]] = if (m.groupCount > 0) Some(1 to m.groupCount map m.group) else None } - /** A class to step through a sequence of regex matches + /** A class to step through a sequence of regex matches. + * + * All methods inherited from [[scala.util.matching.Regex.MatchData]] will throw + * a [[java.lang.IllegalStateException]] until the matcher is initialized. The + * matcher can be initialized by calling `hasNext` or `next()` or causing these + * methods to be called, such as by invoking `toString` or iterating through + * the iterator's elements. + * + * @see [[java.util.regex.Matcher]] */ - class MatchIterator(val source: java.lang.CharSequence, val regex: Regex, val groupNames: Seq[String]) + class MatchIterator(val source: CharSequence, val regex: Regex, val groupNames: Seq[String]) extends AbstractIterator[String] with Iterator[String] with MatchData { self => protected[Regex] val matcher = regex.pattern.matcher(source) @@ -575,7 +680,7 @@ object Regex { nextSeen } - /** The next matched substring of `source` */ + /** The next matched substring of `source`. */ def next(): String = { if (!hasNext) throw new NoSuchElementException nextSeen = false @@ -584,32 +689,32 @@ object Regex { override def toString = super[AbstractIterator].toString - /** The index of the first matched character */ + /** The index of the first matched character. */ def start: Int = matcher.start - /** The index of the first matched character in group `i` */ + /** The index of the first matched character in group `i`. */ def start(i: Int): Int = matcher.start(i) - /** The index of the last matched character */ + /** The index of the last matched character. */ def end: Int = matcher.end - /** The index following the last matched character in group `i` */ + /** The index following the last matched character in group `i`. */ def end(i: Int): Int = matcher.end(i) - /** The number of subgroups */ + /** The number of subgroups. */ def groupCount = matcher.groupCount - /** Convert to an iterator that yields MatchData elements instead of Strings */ + /** Convert to an iterator that yields MatchData elements instead of Strings. */ def matchData: Iterator[Match] = new AbstractIterator[Match] { def hasNext = self.hasNext - def next = { self.next; new Match(source, matcher, groupNames).force } + def next = { self.next(); new Match(source, matcher, groupNames).force } } - /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support */ + /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support. */ private[matching] def replacementData = new AbstractIterator[Match] with Replacement { def matcher = self.matcher def hasNext = self.hasNext - def next = { self.next; new Match(source, matcher, groupNames).force } + def next = { self.next(); new Match(source, matcher, groupNames).force } } } diff --git a/src/library/scala/util/parsing/combinator/RegexParsers.scala b/src/library/scala/util/parsing/combinator/RegexParsers.scala index d17d0cac8d..5f2c07c2ef 100644 --- a/src/library/scala/util/parsing/combinator/RegexParsers.scala +++ b/src/library/scala/util/parsing/combinator/RegexParsers.scala @@ -72,7 +72,7 @@ trait RegexParsers extends Parsers { */ protected def handleWhiteSpace(source: java.lang.CharSequence, offset: Int): Int = if (skipWhitespace) - (whiteSpace findPrefixMatchOf (source.subSequence(offset, source.length))) match { + (whiteSpace findPrefixMatchOf (new SubSequence(source, offset))) match { case Some(matched) => offset + matched.end case None => offset } @@ -106,7 +106,7 @@ trait RegexParsers extends Parsers { val source = in.source val offset = in.offset val start = handleWhiteSpace(source, offset) - (r findPrefixMatchOf (source.subSequence(start, source.length))) match { + (r findPrefixMatchOf (new SubSequence(source, start))) match { case Some(matched) => Success(source.subSequence(start, start + matched.end).toString, in.drop(start + matched.end - offset)) diff --git a/src/library/scala/util/parsing/combinator/SubSequence.scala b/src/library/scala/util/parsing/combinator/SubSequence.scala new file mode 100644 index 0000000000..79c8acac0f --- /dev/null +++ b/src/library/scala/util/parsing/combinator/SubSequence.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util.parsing.combinator + +// A shallow wrapper over another CharSequence (usually a String) +// +// See SI-7710: in jdk7u6 String.subSequence stopped sharing the char array of the original +// string and began copying it. +// RegexParsers calls subSequence twice per input character: that's a lot of array copying! +private[combinator] class SubSequence(s: CharSequence, start: Int, val length: Int) extends CharSequence { + def this(s: CharSequence, start: Int) = this(s, start, s.length - start) + + def charAt(i: Int) = + if (i >= 0 && i < length) s.charAt(start + i) else throw new IndexOutOfBoundsException(s"index: $i, length: $length") + + def subSequence(_start: Int, _end: Int) = { + if (_start < 0 || _end < 0 || _end > length || _start > _end) + throw new IndexOutOfBoundsException(s"start: ${_start}, end: ${_end}, length: $length") + + new SubSequence(s, start + _start, _end - _start) + } + + override def toString = s.subSequence(start, start + length).toString +} diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 032b45316e..80b6b16d0a 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -292,7 +292,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => */ def defaultTargets = symbol.annotations map (_.symbol) filter isMetaAnnotation // Test whether the typeSymbol of atp conforms to the given class. - def matches(clazz: Symbol) = symbol isNonBottomSubClass clazz + def matches(clazz: Symbol) = !symbol.isInstanceOf[StubSymbol] && (symbol isNonBottomSubClass clazz) // All subtrees of all args are considered. def hasArgWhich(p: Tree => Boolean) = args exists (_ exists p) diff --git a/src/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala index 08686832ef..3ab7b20ecd 100644 --- a/src/reflect/scala/reflect/internal/Kinds.scala +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -184,6 +184,7 @@ trait Kinds { ) } else { + hkarg.initialize // SI-7902 otherwise hkarg.typeParams yields List(NoSymbol)! debuglog("checkKindBoundsHK recursing to compare params of "+ hkparam +" with "+ hkarg) kindErrors ++= checkKindBoundsHK( hkarg.typeParams, @@ -229,4 +230,4 @@ trait Kinds { } } } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index fa4441e513..a6556fc22d 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -50,6 +50,11 @@ abstract class TreeInfo { case _ => false } + def isConstructorWithDefault(t: Tree) = t match { + case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, _) => mexists(vparamss)(_.mods.hasDefault) + case _ => false + } + /** Is tree a pure (i.e. non-side-effecting) definition? */ def isPureDef(tree: Tree): Boolean = tree match { diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 53b9b1d88e..ab318bc624 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1315,7 +1315,7 @@ trait Trees extends api.Trees { self: SymbolTable => case Star(elem) => treeCopy.Star(tree, transform(elem)) case UnApply(fun, args) => - treeCopy.UnApply(tree, fun, transformTrees(args)) // bq: see test/.../unapplyContexts2.scala + treeCopy.UnApply(tree, transform(fun), transformTrees(args)) // bq: see test/.../unapplyContexts2.scala case ArrayValue(elemtpt, trees) => treeCopy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) case ApplyDynamic(qual, args) => @@ -1538,6 +1538,21 @@ trait Trees extends api.Trees { self: SymbolTable => def duplicateAndKeepPositions(tree: Tree) = new Duplicator(focusPositions = false) transform tree + def wrapIntoTerm(tree: Tree): Tree = { + if (!tree.isTerm) Block(List(tree), Literal(Constant(()))) else tree + } + + // this is necessary to avoid crashes like https://github.com/scalamacros/paradise/issues/1 + // when someone tries to c.typecheck a naked MemberDef + def wrappingIntoTerm(tree0: Tree)(op: Tree => Tree): Tree = { + val neededWrapping = !tree0.isTerm + val tree1 = wrapIntoTerm(tree0) + op(tree1) match { + case Block(tree2 :: Nil, Literal(Constant(()))) if neededWrapping => tree2 + case tree2 => tree2 + } + } + // ------ copiers ------------------------------------------- def copyDefDef(tree: Tree)( diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 6fdb238462..9c0781ca06 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -685,8 +685,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass) } - def enter(sym: Symbol, mods: Int) = - (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym + def enter(sym: Symbol, mods: Int) = followStatic(clazz, module, mods).info.decls enter sym def enterEmptyCtorIfNecessary(): Unit = { if (jclazz.getConstructors.isEmpty) @@ -733,8 +732,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * If Java modifiers `mods` contain STATIC, return the module class * of the companion module of `clazz`, otherwise the class `clazz` itself. */ - private def followStatic(clazz: Symbol, mods: Int) = - if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz + private def followStatic(clazz: Symbol, mods: Int): Symbol = followStatic(clazz, clazz.companionModule, mods) + + private def followStatic(clazz: Symbol, module: Symbol, mods: Int): Symbol = + // SI-8196 `orElse(clazz)` needed for implementation details of the backend, such as the static + // field containing the cache for structural calls. + if (jModifier.isStatic(mods)) module.moduleClass.orElse(clazz) else clazz /** Methods which need to be treated with care * because they either are getSimpleName or call getSimpleName: diff --git a/test/files/neg/t5923c.check b/test/files/neg/t5923c.check new file mode 100644 index 0000000000..6d3d6b1b78 --- /dev/null +++ b/test/files/neg/t5923c.check @@ -0,0 +1,4 @@ +Test_2.scala:7: error: could not find implicit value for parameter iso: Iso[Test.Foo,L] + val equiv = foo(Foo(23, "foo", true)) + ^ +one error found diff --git a/test/files/neg/t5923c/Macros_1.scala b/test/files/neg/t5923c/Macros_1.scala new file mode 100644 index 0000000000..0b7a3399e2 --- /dev/null +++ b/test/files/neg/t5923c/Macros_1.scala @@ -0,0 +1,39 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +trait Iso[T, U] { + def to(t : T) : U + // def from(u : U) : T +} + +object Iso { + implicit def materializeIso[T, U]: Iso[T, U] = macro impl[T, U] + def impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context): c.Expr[Iso[T, U]] = { + import c.universe._ + import definitions._ + import Flag._ + + val sym = c.weakTypeOf[T].typeSymbol + if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class") + val fields = sym.typeSignature.declarations.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x } + + def mkTpt() = { + val core = Ident(TupleClass(fields.length) orElse UnitClass) + if (fields.length == 0) core + else AppliedTypeTree(core, fields map (f => TypeTree(f.typeSignature))) + } + + def mkFrom() = { + if (fields.length == 0) Literal(Constant(Unit)) + else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim)))) + } + + val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template( + List(AppliedTypeTree(Ident(newTypeName("Iso")), List(Ident(sym), mkTpt()))), + emptyValDef, + List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom())))) + c.Expr[Iso[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))) + } +} diff --git a/test/files/neg/t5923c/Test_2.scala b/test/files/neg/t5923c/Test_2.scala new file mode 100644 index 0000000000..a00f4ed7db --- /dev/null +++ b/test/files/neg/t5923c/Test_2.scala @@ -0,0 +1,12 @@ +// see the comments for macroExpandApply.onDelayed for an explanation of what's tested here +object Test extends App { + case class Foo(i: Int, s: String, b: Boolean) + def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) + + { + val equiv = foo(Foo(23, "foo", true)) + def typed[T](t: => T) {} + typed[(Int, String, Boolean)](equiv) + println(equiv) + } +}
\ No newline at end of file diff --git a/test/files/pos/t7902.scala b/test/files/pos/t7902.scala new file mode 100644 index 0000000000..47c525c179 --- /dev/null +++ b/test/files/pos/t7902.scala @@ -0,0 +1,17 @@ +import scala.language.higherKinds + +object Bug { + class Tag[W[M1[X1]]] + + def ofType[W[M2[X2]]]: Tag[W] = ??? + type InSeq [M3[X3]] = Some[M3[Any]] + + // fail + val x = ofType[InSeq] + + // okay + val y: Any = ofType[InSeq] + object T { + val z = ofType[InSeq] + } +} diff --git a/test/files/pos/t8596.flags b/test/files/pos/t8596.flags new file mode 100644 index 0000000000..281f0a10cd --- /dev/null +++ b/test/files/pos/t8596.flags @@ -0,0 +1 @@ +-Yrangepos diff --git a/test/files/pos/t8596.scala b/test/files/pos/t8596.scala new file mode 100644 index 0000000000..bfed58eadf --- /dev/null +++ b/test/files/pos/t8596.scala @@ -0,0 +1,7 @@ +class TypeTreeObjects { + class Container { + def typeParamAndDefaultArg[C](name: String = ""): String = "" + } + // crashed under -Yrangepos + new Container().typeParamAndDefaultArg[Any]() +} diff --git a/test/files/presentation/t7915.check b/test/files/presentation/t7915.check index b18b4ddb55..0849aaa82b 100644 --- a/test/files/presentation/t7915.check +++ b/test/files/presentation/t7915.check @@ -9,3 +9,23 @@ askHyperlinkPos for `bar` at (7,22) Foo.scala ================================================================================ [response] found askHyperlinkPos for `bar` at (2,7) Foo.scala ================================================================================ + +askHyperlinkPos for `Bar` at (8,11) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `Bar` at (1,7) Foo.scala +================================================================================ + +askHyperlinkPos for `baz` at (8,22) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `baz` at (2,31) Foo.scala +================================================================================ + +askHyperlinkPos for `Bar` at (9,11) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `Bar` at (1,7) Foo.scala +================================================================================ + +askHyperlinkPos for `baz` at (9,22) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `baz` at (2,31) Foo.scala +================================================================================ diff --git a/test/files/presentation/t7915/src/Foo.scala b/test/files/presentation/t7915/src/Foo.scala index a4166ae5b4..5c9ca36a6e 100644 --- a/test/files/presentation/t7915/src/Foo.scala +++ b/test/files/presentation/t7915/src/Foo.scala @@ -1,9 +1,11 @@ class Bar { - def bar(b: Int = 2) {} + def bar(b: Int = 2) {}; def baz[X](b: Int = 2) {} } class Foo { def foo() { new Bar/*#*/().bar/*#*/() + new Bar/*#*/().baz/*#*/[Any]() + new Bar/*#*/().baz/*#*/() } } diff --git a/test/files/run/macro-vampire-false-warning.check b/test/files/run/macro-vampire-false-warning.check new file mode 100644 index 0000000000..4792e70f33 --- /dev/null +++ b/test/files/run/macro-vampire-false-warning.check @@ -0,0 +1,2 @@ +2 +3 diff --git a/test/files/run/macro-vampire-false-warning.flags b/test/files/run/macro-vampire-false-warning.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/run/macro-vampire-false-warning.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/run/macro-vampire-false-warning/Macros_1.scala b/test/files/run/macro-vampire-false-warning/Macros_1.scala new file mode 100644 index 0000000000..a1c40a5510 --- /dev/null +++ b/test/files/run/macro-vampire-false-warning/Macros_1.scala @@ -0,0 +1,52 @@ +// As per http://meta.plasm.us/posts/2013/08/31/feeding-our-vampires/ + +import scala.annotation.StaticAnnotation +import scala.reflect.macros.Context +import scala.language.experimental.macros + +class body(tree: Any) extends StaticAnnotation + +object Macros { + def selFieldImpl(c: Context) = { + import c.universe._ + val field = c.macroApplication.symbol + val bodyAnn = field.annotations.filter(_.tpe <:< typeOf[body]).head + c.Expr[Any](bodyAnn.scalaArgs.head) + } + + def mkObjectImpl(c: Context)(xs: c.Expr[Any]*) = { + import c.universe._ + import Flag._ + // val kvps = xs.toList map { case q"${_}(${Literal(Constant(name: String))}).->[${_}]($value)" => name -> value } + val kvps = xs.map(_.tree).toList map { case Apply(TypeApply(Select(Apply(_, List(Literal(Constant(name: String)))), _), _), List(value)) => name -> value } + // val fields = kvps map { case (k, v) => q"@body($v) def ${TermName(k)} = macro Macros.selFieldImpl" } + val fields = kvps map { case (k, v) => DefDef( + Modifiers(MACRO, tpnme.EMPTY, List(Apply(Select(New(Ident(newTypeName("body"))), nme.CONSTRUCTOR), List(v)))), + newTermName(k), Nil, Nil, TypeTree(), Select(Ident(newTermName("Macros")), newTermName("selFieldImpl"))) } + // q"import scala.language.experimental.macros; class Workaround { ..$fields }; new Workaround{}" + c.Expr[Any](Block( + List( + Import(Select(Select(Ident(newTermName("scala")), newTermName("language")), newTermName("experimental")), List(ImportSelector(newTermName("macros"), 51, newTermName("macros"), 51))), + ClassDef( + NoMods, newTypeName("Workaround"), Nil, + Template( + List(Select(Ident(newTermName("scala")), newTypeName("AnyRef"))), emptyValDef, + DefDef( + NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(), + Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))) + +: fields)), + ClassDef( + Modifiers(FINAL), newTypeName("$anon"), Nil, + Template( + List(Ident(newTypeName("Workaround"))), emptyValDef, + List( + DefDef( + NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(), + Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))))))), + Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))) + } +} + +object mkObject { + def apply(xs: Any*) = macro Macros.mkObjectImpl +} diff --git a/test/files/run/macro-vampire-false-warning/Test_2.scala b/test/files/run/macro-vampire-false-warning/Test_2.scala new file mode 100644 index 0000000000..6e44b68635 --- /dev/null +++ b/test/files/run/macro-vampire-false-warning/Test_2.scala @@ -0,0 +1,6 @@ +object Test extends App { + val foo = mkObject("x" -> "2", "y" -> 3) + println(foo.x) + println(foo.y) + // println(foo.z) => will result in a compilation error +}
\ No newline at end of file diff --git a/test/files/run/t5923a.check b/test/files/run/t5923a-fundep.check index 7165b734ac..7165b734ac 100644 --- a/test/files/run/t5923a.check +++ b/test/files/run/t5923a-fundep.check diff --git a/test/files/run/t5923a-fundep.flags b/test/files/run/t5923a-fundep.flags new file mode 100644 index 0000000000..384b48d1b0 --- /dev/null +++ b/test/files/run/t5923a-fundep.flags @@ -0,0 +1 @@ +-Yfundep-materialization
\ No newline at end of file diff --git a/test/files/run/t5923a-fundep/Macros_1.scala b/test/files/run/t5923a-fundep/Macros_1.scala new file mode 100644 index 0000000000..97076eb102 --- /dev/null +++ b/test/files/run/t5923a-fundep/Macros_1.scala @@ -0,0 +1,52 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +case class C[T](t: String) +object C { + implicit def foo[T]: C[T] = macro Macros.impl[T] +} + +object Macros { + def impl[T](c: Context)(ttag: c.WeakTypeTag[T]) = { + import c.universe._ + val ttag0 = ttag; + { + // When we're expanding implicitly[C[Nothing]], the type inferencer will see + // that foo[T] returns C[T] and that we request an implicit of type C[Nothing]. + // + // Then the type inferencer will try to match C[T] against C[Nothing] and infer everything it can infer + // from that match, but not more (e.g. if we were returning Iso[T, U] and the type we were looking at was Iso[Foo, L], + // we wouldn't want U to be auto-inferred to Nothing, as it usually happens with normal methods, + // but would rather want it to remain unknown, so that our macro could take a stab at inferring it: + // see the comments in this commit for more information). + // + // Equipped with common sense, in our case of C[T] and C[Nothing] we would expect T to be inferred as Nothing, and then we + // would expect T in the corresponding macro invocation to be Nothing. Unfortunately it is not that simple. + // + // Internally the type inferencer uses Nothing as a dummy value, which stands for "don't know how to + // infer this type parameter". In the Iso example, matching Iso[T, U] against Iso[Foo, L] would result in + // T being inferred as Foo and U being inferred as Nothing (!!). Then the type inferencer will think: + // "Aha! U ended up being Nothing. This means that I failed to infer it, + // therefore the result of my work is: T -> Foo, U -> still unknown". + // + // That's all very good and works very well until Nothing is a genuine result of type inference, + // as in our original example of inferring T in C[T] from C[Nothing]. In that case, the inferencer becomes confused + // and here in the macro implementation we get weakTypeOf[T] equal to some dummy type carrying a type parameter + // instead of Nothing. + // + // This eccentric behavior of the type inferencer is a long-standing problem in scalac, + // so the best one can do for now until it's fixed is to work around, manually converting + // suspicious T's into Nothings. Of course, this means that we would have to approximate, + // because there's no way to know whether having T here stands for a failed attempt to infer Nothing + // or for a failed attempt to infer anything, but at least we're in full control of making the best + // of this sad situation. + implicit def ttag: WeakTypeTag[T] = { + val tpe = ttag0.tpe + val sym = tpe.typeSymbol.asType + if (sym.isParameter && !sym.isSkolem) TypeTag.Nothing.asInstanceOf[TypeTag[T]] + else ttag0 + } + reify(C[T](c.literal(weakTypeOf[T].toString).splice)) + } + } +}
\ No newline at end of file diff --git a/test/files/run/t5923a/Test_2.scala b/test/files/run/t5923a-fundep/Test_2.scala index 001ff9aea8..001ff9aea8 100644 --- a/test/files/run/t5923a/Test_2.scala +++ b/test/files/run/t5923a-fundep/Test_2.scala diff --git a/test/files/run/t5923a-nofundep.check b/test/files/run/t5923a-nofundep.check new file mode 100644 index 0000000000..7165b734ac --- /dev/null +++ b/test/files/run/t5923a-nofundep.check @@ -0,0 +1,3 @@ +C(Int) +C(String) +C(Nothing) diff --git a/test/files/run/t5923a/Macros_1.scala b/test/files/run/t5923a-nofundep/Macros_1.scala index 6d21362c4d..6d21362c4d 100644 --- a/test/files/run/t5923a/Macros_1.scala +++ b/test/files/run/t5923a-nofundep/Macros_1.scala diff --git a/test/files/run/t5923a-nofundep/Test_2.scala b/test/files/run/t5923a-nofundep/Test_2.scala new file mode 100644 index 0000000000..001ff9aea8 --- /dev/null +++ b/test/files/run/t5923a-nofundep/Test_2.scala @@ -0,0 +1,5 @@ +object Test extends App { + println(implicitly[C[Int]]) + println(implicitly[C[String]]) + println(implicitly[C[Nothing]]) +}
\ No newline at end of file diff --git a/test/files/run/t5923c.check b/test/files/run/t5923c.check new file mode 100644 index 0000000000..bed7429108 --- /dev/null +++ b/test/files/run/t5923c.check @@ -0,0 +1 @@ +(23,foo,true) diff --git a/test/files/run/t5923c.flags b/test/files/run/t5923c.flags new file mode 100644 index 0000000000..384b48d1b0 --- /dev/null +++ b/test/files/run/t5923c.flags @@ -0,0 +1 @@ +-Yfundep-materialization
\ No newline at end of file diff --git a/test/files/run/t5923c/Macros_1.scala b/test/files/run/t5923c/Macros_1.scala new file mode 100644 index 0000000000..0b7a3399e2 --- /dev/null +++ b/test/files/run/t5923c/Macros_1.scala @@ -0,0 +1,39 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +trait Iso[T, U] { + def to(t : T) : U + // def from(u : U) : T +} + +object Iso { + implicit def materializeIso[T, U]: Iso[T, U] = macro impl[T, U] + def impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context): c.Expr[Iso[T, U]] = { + import c.universe._ + import definitions._ + import Flag._ + + val sym = c.weakTypeOf[T].typeSymbol + if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class") + val fields = sym.typeSignature.declarations.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x } + + def mkTpt() = { + val core = Ident(TupleClass(fields.length) orElse UnitClass) + if (fields.length == 0) core + else AppliedTypeTree(core, fields map (f => TypeTree(f.typeSignature))) + } + + def mkFrom() = { + if (fields.length == 0) Literal(Constant(Unit)) + else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim)))) + } + + val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template( + List(AppliedTypeTree(Ident(newTypeName("Iso")), List(Ident(sym), mkTpt()))), + emptyValDef, + List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom())))) + c.Expr[Iso[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))) + } +} diff --git a/test/files/run/t5923c/Test_2.scala b/test/files/run/t5923c/Test_2.scala new file mode 100644 index 0000000000..a00f4ed7db --- /dev/null +++ b/test/files/run/t5923c/Test_2.scala @@ -0,0 +1,12 @@ +// see the comments for macroExpandApply.onDelayed for an explanation of what's tested here +object Test extends App { + case class Foo(i: Int, s: String, b: Boolean) + def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) + + { + val equiv = foo(Foo(23, "foo", true)) + def typed[T](t: => T) {} + typed[(Int, String, Boolean)](equiv) + println(equiv) + } +}
\ No newline at end of file diff --git a/test/files/run/t5923d.check b/test/files/run/t5923d.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t5923d.check diff --git a/test/files/run/t5923d/Macros_1.scala b/test/files/run/t5923d/Macros_1.scala new file mode 100644 index 0000000000..f32d1af704 --- /dev/null +++ b/test/files/run/t5923d/Macros_1.scala @@ -0,0 +1,9 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Context + +trait MappedRow +trait RowMapper[T <: MappedRow] +object RowMapper { + implicit def mapper[T <: MappedRow]: RowMapper[T] = macro impl[T] + def impl[T <: MappedRow : c.WeakTypeTag](c: Context) = c.universe.reify(new RowMapper[T]{}) +}
\ No newline at end of file diff --git a/test/files/run/t5923d/Test_2.scala b/test/files/run/t5923d/Test_2.scala new file mode 100644 index 0000000000..6be10227c2 --- /dev/null +++ b/test/files/run/t5923d/Test_2.scala @@ -0,0 +1,7 @@ +class RowA extends MappedRow +class RowB extends MappedRow + +object Test extends App { + implicitly[RowMapper[RowA]] + implicitly[RowMapper[RowB]] +}
\ No newline at end of file diff --git a/test/files/run/t7871.check b/test/files/run/t7871.check new file mode 100644 index 0000000000..ce6efd812d --- /dev/null +++ b/test/files/run/t7871.check @@ -0,0 +1 @@ +(SomeTree,SomeTree) diff --git a/test/files/run/t7871/Macros_1.scala b/test/files/run/t7871/Macros_1.scala new file mode 100644 index 0000000000..2943445ff8 --- /dev/null +++ b/test/files/run/t7871/Macros_1.scala @@ -0,0 +1,43 @@ +import scala.reflect.macros.Context +import scala.language.experimental.macros + +trait Tree +case object SomeTree extends Tree + +object NewQuasiquotes { + implicit class QuasiquoteInterpolation(c: StringContext) { + object nq { + def unapply(t: Tree): Any = macro QuasiquoteMacros.unapplyImpl + } + } +} + +object QuasiquoteMacros { + def unapplyImpl(c: Context)(t: c.Expr[Tree]) = { + import c.universe._ + import Flag._ + // q""" + // new { + // def unapply(t: Tree) = t match { + // case SomeTree => Some((SomeTree, SomeTree)) + // case _ => None + // } + // }.unapply($t) + // """ + c.Expr[Any](Apply( + Select( + Block(List( + ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), + Template(List(Select(Ident(newTermName("scala")), newTypeName("AnyRef"))), emptyValDef, List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), + Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + DefDef(Modifiers(), newTermName("unapply"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("t"), Ident(newTypeName("Tree")), EmptyTree))), TypeTree(), + Match( + Ident(newTermName("t")), List( + CaseDef(Ident(newTermName("SomeTree")), EmptyTree, Apply(Ident(newTermName("Some")), List(Apply(Select(Ident(newTermName("scala")), newTermName("Tuple2")), List(Ident(newTermName("SomeTree")), Ident(newTermName("SomeTree"))))))), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Ident(newTermName("None")))))))))), + Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List())), + newTermName("unapply")), + List(t.tree))) + } +} diff --git a/test/files/run/t7871/Test_2.scala b/test/files/run/t7871/Test_2.scala new file mode 100644 index 0000000000..3a0b68b568 --- /dev/null +++ b/test/files/run/t7871/Test_2.scala @@ -0,0 +1,6 @@ +object Test extends App { + import NewQuasiquotes._ + SomeTree match { + case nq"$x + $y" => println((x, y)) + } +} diff --git a/test/files/run/t8196.check b/test/files/run/t8196.check new file mode 100644 index 0000000000..3286c15c91 --- /dev/null +++ b/test/files/run/t8196.check @@ -0,0 +1,3 @@ +Scope{ + final private val f1: Int +} diff --git a/test/files/run/t8196.scala b/test/files/run/t8196.scala new file mode 100644 index 0000000000..e219ac166b --- /dev/null +++ b/test/files/run/t8196.scala @@ -0,0 +1,51 @@ +import scala.reflect.runtime.{ universe => ru } + +object Test extends App { + + trait FormTrait { + + val runtimeMirror = ru.runtimeMirror(this.getClass.getClassLoader) + val instanceMirror = runtimeMirror.reflect(this) + val members = instanceMirror.symbol.typeSignature.members + def fields = members.filter(_.typeSignature <:< ru.typeOf[Int]) + } + + val f = () => { + + class Form1 extends FormTrait { + val f1 = 5 + } + val form1 = new Form1 + + println(form1.fields) + + val form2 = new FormTrait { + val g1 = new Form1 + } + + form2.g1 // comment this line in order to make the test pass + () + } + + val g = () => { + // Reported as SI-8195, same root cause + trait Form { + + private val runtimeMirror = ru.runtimeMirror(this.getClass.getClassLoader) + private val instanceMirror = runtimeMirror.reflect(this) + private val members = instanceMirror.symbol.typeSignature.members + + } + + val f1 = new Form { + val a = 1 + } + + val f2 = new Form { + val b = f1.a + } + } + + f() + g() +} diff --git a/test/files/run/t8442.check b/test/files/run/t8442.check new file mode 100644 index 0000000000..ce9e8b52ff --- /dev/null +++ b/test/files/run/t8442.check @@ -0,0 +1 @@ +pos: NoPosition Class A_1 not found - continuing with a stub. WARNING diff --git a/test/files/run/t8442/A_1.java b/test/files/run/t8442/A_1.java new file mode 100644 index 0000000000..227451eecd --- /dev/null +++ b/test/files/run/t8442/A_1.java @@ -0,0 +1,4 @@ +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface A_1 { + +}
\ No newline at end of file diff --git a/test/files/run/t8442/B_1.java b/test/files/run/t8442/B_1.java new file mode 100644 index 0000000000..1680684495 --- /dev/null +++ b/test/files/run/t8442/B_1.java @@ -0,0 +1,3 @@ +public class B_1 { + @A_1 public String get() { return ""; } +} diff --git a/test/files/run/t8442/C_2.scala b/test/files/run/t8442/C_2.scala new file mode 100644 index 0000000000..d75d4bd910 --- /dev/null +++ b/test/files/run/t8442/C_2.scala @@ -0,0 +1,5 @@ +class C_2 { + def foo(b: B_1) { + b.get() + } +} diff --git a/test/files/run/t8442/Test.scala b/test/files/run/t8442/Test.scala new file mode 100644 index 0000000000..ff6da4e206 --- /dev/null +++ b/test/files/run/t8442/Test.scala @@ -0,0 +1,29 @@ +import scala.tools.partest._ +import java.io.File + +object Test extends StoreReporterDirectTest { + def code = ??? + + def compileCode(code: String) = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code) + } + + def app = """ + class C_2 { + def foo(b: B_1) { + b.get() + } + } + """ + + def show(): Unit = { + val tClass = new File(testOutput.path, "A_1.class") + assert(tClass.exists) + assert(tClass.delete()) + + // Expecting stub symbol warning, but no stack trace! + compileCode(app) + println(filteredInfos.mkString("\n")) + } +} diff --git a/test/files/run/typecheck.check b/test/files/run/typecheck.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/typecheck.check diff --git a/test/files/run/typecheck/Macros_1.scala b/test/files/run/typecheck/Macros_1.scala new file mode 100644 index 0000000000..850a611ab1 --- /dev/null +++ b/test/files/run/typecheck/Macros_1.scala @@ -0,0 +1,17 @@ +import scala.reflect.macros.Context +import scala.language.experimental.macros + +object Macros { + def impl(c: Context) = { + import c.universe._ + val classDef = ClassDef( + Modifiers(), newTypeName("C"), List(), + Template( + List(Select(Ident(newTermName("scala")), newTypeName("AnyRef"))), emptyValDef, + List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(()))))))) + c.typeCheck(classDef) + c.Expr[Any](Literal(Constant(()))) + } + + def foo: Any = macro impl +}
\ No newline at end of file diff --git a/test/files/run/typecheck/Test_2.scala b/test/files/run/typecheck/Test_2.scala new file mode 100644 index 0000000000..0a3279e23e --- /dev/null +++ b/test/files/run/typecheck/Test_2.scala @@ -0,0 +1,15 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +object Test extends App { + Macros.foo + + val tb = cm.mkToolBox() + val classDef = ClassDef( + Modifiers(), newTypeName("C"), List(), + Template( + List(Select(Ident(newTermName("scala")), newTypeName("AnyRef"))), emptyValDef, + List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(()))))))) + tb.typeCheck(classDef) +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-8479.check b/test/scaladoc/run/SI-8479.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-8479.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-8479.scala b/test/scaladoc/run/SI-8479.scala new file mode 100755 index 0000000000..3c91395025 --- /dev/null +++ b/test/scaladoc/run/SI-8479.scala @@ -0,0 +1,32 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.base._ +import scala.tools.nsc.doc.base.comment._ +import scala.tools.partest.ScaladocModelTest +import java.net.{URI, URL} +import java.io.File + +object Test extends ScaladocModelTest { + + override def code = + """ + |object Test { + | val x = new SparkContext(master = "") + |} + | + |class SparkContext(config: Any) { + | + | /** Scaladoc comment */ + | def this( + | master: String, + | appName: String = "") = this(null) + |} + | + | + """.stripMargin + + override def scaladocSettings = "" + + def testModel(rootPackage: Package) { + // it didn't crash + } +} diff --git a/test/scaladoc/run/t8314.check b/test/scaladoc/run/t8314.check new file mode 100644 index 0000000000..aa04c12c8f --- /dev/null +++ b/test/scaladoc/run/t8314.check @@ -0,0 +1,3 @@ +Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This should be ), Monospace(Text(monospaced)))))))))) + +Done. diff --git a/test/scaladoc/run/t8314.scala b/test/scaladoc/run/t8314.scala new file mode 100644 index 0000000000..7f6d6fdb00 --- /dev/null +++ b/test/scaladoc/run/t8314.scala @@ -0,0 +1,16 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + override def code = """ + /** This should be `monospaced` */ + class A + """ + + def scaladocSettings = "" + + def testModel(root: Package) = { + import access._ + root._class("A").comment foreach println + } +} |