summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bincompat-forward.whitelist.conf9
-rw-r--r--build.number2
-rw-r--r--src/compiler/scala/reflect/macros/runtime/Typers.scala16
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeInfo.scala10
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala10
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala33
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala2
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala203
-rw-r--r--src/library/scala/runtime/SeqCharSequence.scala7
-rw-r--r--src/library/scala/util/matching/Regex.scala477
-rw-r--r--src/library/scala/util/parsing/combinator/RegexParsers.scala4
-rw-r--r--src/library/scala/util/parsing/combinator/SubSequence.scala32
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Kinds.scala3
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala5
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala17
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala11
-rw-r--r--test/files/neg/t5923c.check4
-rw-r--r--test/files/neg/t5923c/Macros_1.scala39
-rw-r--r--test/files/neg/t5923c/Test_2.scala12
-rw-r--r--test/files/pos/t7902.scala17
-rw-r--r--test/files/pos/t8596.flags1
-rw-r--r--test/files/pos/t8596.scala7
-rw-r--r--test/files/presentation/t7915.check20
-rw-r--r--test/files/presentation/t7915/src/Foo.scala4
-rw-r--r--test/files/run/macro-vampire-false-warning.check2
-rw-r--r--test/files/run/macro-vampire-false-warning.flags1
-rw-r--r--test/files/run/macro-vampire-false-warning/Macros_1.scala52
-rw-r--r--test/files/run/macro-vampire-false-warning/Test_2.scala6
-rw-r--r--test/files/run/t5923a-fundep.check (renamed from test/files/run/t5923a.check)0
-rw-r--r--test/files/run/t5923a-fundep.flags1
-rw-r--r--test/files/run/t5923a-fundep/Macros_1.scala52
-rw-r--r--test/files/run/t5923a-fundep/Test_2.scala (renamed from test/files/run/t5923a/Test_2.scala)0
-rw-r--r--test/files/run/t5923a-nofundep.check3
-rw-r--r--test/files/run/t5923a-nofundep/Macros_1.scala (renamed from test/files/run/t5923a/Macros_1.scala)0
-rw-r--r--test/files/run/t5923a-nofundep/Test_2.scala5
-rw-r--r--test/files/run/t5923c.check1
-rw-r--r--test/files/run/t5923c.flags1
-rw-r--r--test/files/run/t5923c/Macros_1.scala39
-rw-r--r--test/files/run/t5923c/Test_2.scala12
-rw-r--r--test/files/run/t5923d.check0
-rw-r--r--test/files/run/t5923d/Macros_1.scala9
-rw-r--r--test/files/run/t5923d/Test_2.scala7
-rw-r--r--test/files/run/t7871.check1
-rw-r--r--test/files/run/t7871/Macros_1.scala43
-rw-r--r--test/files/run/t7871/Test_2.scala6
-rw-r--r--test/files/run/t8196.check3
-rw-r--r--test/files/run/t8196.scala51
-rw-r--r--test/files/run/t8442.check1
-rw-r--r--test/files/run/t8442/A_1.java4
-rw-r--r--test/files/run/t8442/B_1.java3
-rw-r--r--test/files/run/t8442/C_2.scala5
-rw-r--r--test/files/run/t8442/Test.scala29
-rw-r--r--test/files/run/typecheck.check0
-rw-r--r--test/files/run/typecheck/Macros_1.scala17
-rw-r--r--test/files/run/typecheck/Test_2.scala15
-rw-r--r--test/scaladoc/run/SI-8479.check1
-rwxr-xr-xtest/scaladoc/run/SI-8479.scala32
-rw-r--r--test/scaladoc/run/t8314.check3
-rw-r--r--test/scaladoc/run/t8314.scala16
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
+ }
+}