diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/ast')
13 files changed, 343 insertions, 191 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index 6442ef2d54..c70690e697 100644 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -129,25 +129,6 @@ trait DocComments { self: Global => getDocComment(sym) map getUseCases getOrElse List() } - private val wikiReplacements = List( - ("""(\n\s*\*?)(\s*\n)""" .r, """$1 <p>$2"""), - ("""<([^\w/])""" .r, """<$1"""), - ("""([^\w/])>""" .r, """$1>"""), - ("""\{\{\{(.*(?:\n.*)*)\}\}\}""".r, """<pre>$1</pre>"""), - ("""`([^`]*)`""" .r, """<code>$1</code>"""), - ("""__([^_]*)__""" .r, """<u>$1</u>"""), - ("""''([^']*)''""" .r, """<i>$1</i>"""), - ("""'''([^']*)'''""" .r, """<b>$1</b>"""), - ("""\^([^^]*)\^""" .r, """<sup>$1</sup>"""), - (""",,([^,]*),,""" .r, """<sub>$1</sub>""")) - - /** Returns just the wiki expansion (this would correspond to - * a comment in the input format of the JavaDoc tool, modulo differences - * in tags.) - */ - def expandWiki(str: String): String = - (str /: wikiReplacements) { (str1, regexRepl) => regexRepl._1 replaceAllIn(str1, regexRepl._2) } - private def getDocComment(sym: Symbol): Option[DocComment] = mapFind(sym :: allInheritedOverriddenSymbols(sym))(docComments get _) diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala index f3def3c80c..8b37948e9b 100644 --- a/src/compiler/scala/tools/nsc/ast/Printers.scala +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package ast -import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import java.io.{ OutputStream, PrintWriter } trait Printers extends scala.reflect.internal.Printers { this: Global => diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index eafecf9462..105bdee256 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -7,7 +7,9 @@ package scala package tools.nsc package ast -import java.awt.{List => awtList, _} +import scala.language.implicitConversions + +import java.awt.{List => _, _} import java.awt.event._ import java.io.StringWriter @@ -17,7 +19,6 @@ import javax.swing.tree._ import scala.concurrent.Lock import scala.text._ -import scala.language.implicitConversions /** * Tree browsers can show the AST in a graphical and interactive diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 6dda30b5e7..9e1498cf3e 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package ast -import symtab.Flags import scala.language.implicitConversions /** A DSL for generating scala code. The goal is that the diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 332acf4a26..b073cb828c 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -91,7 +91,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { ) /** Make a synchronized block on 'monitor'. */ - def mkSynchronized(monitor: Tree, body: Tree): Tree = + def mkSynchronized(monitor: Tree)(body: Tree): Tree = Apply(Select(monitor, Object_synchronized), List(body)) def mkAppliedTypeForCase(clazz: Symbol): Tree = { @@ -145,6 +145,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { override def mkCast(tree: Tree, pt: Type): Tree = { debuglog("casting " + tree + ":" + tree.tpe + " to " + pt + " at phase: " + phase) assert(!tree.tpe.isInstanceOf[MethodType], tree) + assert(!pt.isInstanceOf[MethodType], tree) assert(pt eq pt.normalize, tree +" : "+ debugString(pt) +" ~>"+ debugString(pt.normalize)) atPos(tree.pos) { mkAsInstanceOf(tree, pt, any = !phase.next.erasedTypes, wrapInApply = isAtPhaseAfter(currentRun.uncurryPhase)) @@ -232,22 +233,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { else Block(prefix, containing) setPos (prefix.head.pos union containing.pos) } - /** Return the synchronized part of the double-checked locking idiom around the syncBody tree. It guards with `cond` and - * synchronizes on `clazz.this`. Additional statements can be included after initialization, - * (outside the synchronized block). - * - * The idiom works only if the condition is using a volatile field. - * @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html - */ - def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = - mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats) - - def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = - Block(mkSynchronized( - attrThis, - If(cond, Block(syncBody: _*), EmptyTree)) :: - stats: _*) - /** Creates a tree representing new Object { stats }. * To make sure an anonymous subclass of Object is created, * if there are no stats, a () is added. @@ -257,43 +242,135 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { mkNew(Nil, noSelfType, stats1, NoPosition, NoPosition) } - /** - * Create a method based on a Function - * - * Used both to under `-Ydelambdafy:method` create a lifted function and - * under `-Ydelambdafy:inline` to create the apply method on the anonymous - * class. - * - * It creates a method definition with value params cloned from the - * original lambda. Then it calls a supplied function to create - * the body and types the result. Finally - * everything is wrapped up in a DefDef - * - * @param owner The owner for the new method - * @param name name for the new method - * @param additionalFlags flags to be put on the method in addition to FINAL - */ - def mkMethodFromFunction(localTyper: analyzer.Typer) - (fun: Function, owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags) = { - val funParams = fun.vparams map (_.symbol) - val formals :+ restpe = fun.tpe.typeArgs - val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) + // Construct a method to implement `fun`'s single abstract method (`apply`, when `fun.tpe` is a built-in function type) + def mkMethodFromFunction(localTyper: analyzer.Typer)(owner: Symbol, fun: Function) = { + // TODO: treat FunctionN like any other SAM -- drop `&& !isFunctionType(fun.tpe)` + val sam = if (!isFunctionType(fun.tpe)) samOf(fun.tpe) else NoSymbol + if (!sam.exists) mkMethodForFunctionBody(localTyper)(owner, fun, nme.apply)() + else { + val samMethType = fun.tpe memberInfo sam + mkMethodForFunctionBody(localTyper)(owner, fun, sam.name.toTermName)(methParamProtos = samMethType.params, resTp = samMethType.resultType) + } + } - val paramSyms = map2(formals, fun.vparams) { - (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) + // used to create the lifted method that holds a function's body + def mkLiftedFunctionBodyMethod(localTyper: global.analyzer.Typer)(owner: global.Symbol, fun: global.Function) = { + def nonLocalEnclosingMember(sym: Symbol): Symbol = { + if (sym.isLocalDummy) sym.enclClass.primaryConstructor + else if (sym.isLocalToBlock) nonLocalEnclosingMember(sym.originalOwner) + else sym } + val ownerName = nonLocalEnclosingMember(fun.symbol.originalOwner).name match { + case nme.CONSTRUCTOR => nme.NEWkw // do as javac does for the suffix, prefer "new" to "$lessinit$greater$1" + case x => x.dropLocal + } + val newName = nme.ANON_FUN_NAME.append(nme.NAME_JOIN_STRING).append(ownerName) + mkMethodForFunctionBody(localTyper)(owner, fun, newName)(additionalFlags = ARTIFACT) + } - methSym setInfo MethodType(paramSyms, restpe.deconst) - fun.body.substituteSymbols(funParams, paramSyms) - fun.body changeOwner (fun.symbol -> methSym) + // the result type of a function or corresponding SAM type + private def functionResultType(tp: Type): Type = { + val dealiased = tp.dealiasWiden + if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.last + else samOf(tp) match { + case samSym if samSym.exists => tp.memberInfo(samSym).resultType.deconst + case _ => NoType + } + } - val methDef = DefDef(methSym, fun.body) + /** + * Lift a Function's body to a method. For use during Uncurry, where Function nodes have type FunctionN[T1, ..., Tn, R] + * + * It creates a method definition with value params derived from the original lambda + * or `methParamProtos` (used to create the correct override for sam methods). + * + * Replace the `fun.vparams` symbols by the newly created method params, + * changes owner of `fun.body` from `fun.symbol` to resulting method's symbol. + * + * @param owner The owner for the new method + * @param fun the function to take the body from + * @param name name for the new method + * @param additionalFlags flags to be put on the method in addition to FINAL + */ + private def mkMethodForFunctionBody(localTyper: analyzer.Typer) + (owner: Symbol, fun: Function, name: TermName) + (methParamProtos: List[Symbol] = fun.vparams.map(_.symbol), + resTp: Type = functionResultType(fun.tpe), + additionalFlags: FlagSet = NoFlags): DefDef = { + val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) + // for sams, methParamProtos is the parameter symbols for the sam's method, so that we generate the correct override (based on parameter types) + val methParamSyms = methParamProtos.map { param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName) } + methSym setInfo MethodType(methParamSyms, resTp) + + // we must rewire reference to the function's param symbols -- and not methParamProtos -- to methParamSyms + val useMethodParams = new TreeSymSubstituter(fun.vparams.map(_.symbol), methParamSyms) + // we're now owned by the method that holds the body, and not the function + val moveToMethod = new ChangeOwnerTraverser(fun.symbol, methSym) + + newDefDef(methSym, moveToMethod(useMethodParams(fun.body)))(tpt = TypeTree(resTp)) + } + + /** + * Create a new `DefDef` based on `orig` with an explicit self parameter. + * + * Details: + * - Must by run after erasure + * - If `maybeClone` is the identity function, this runs "in place" + * and mutates the symbol of `orig`. `orig` should be discarded + * - Symbol owners and returns are substituted, as are parameter symbols + * - Recursive calls are not rewritten. This is correct if we assume + * that we either: + * - are in "in-place" mode, but can guarantee that no recursive calls exists + * - are associating the RHS with a cloned symbol, but intend for the original + * method to remain and for recursive calls to target it. + */ + final def mkStatic(orig: DefDef, newName: Name, maybeClone: Symbol => Symbol): DefDef = { + assert(phase.erasedTypes, phase) + assert(!orig.symbol.hasFlag(SYNCHRONIZED), orig.symbol.defString) + val origSym = orig.symbol + val origParams = orig.symbol.info.params + val newSym = maybeClone(orig.symbol) + newSym.setName(newName) + newSym.setFlag(STATIC) + // Add an explicit self parameter + val selfParamSym = newSym.newSyntheticValueParam(newSym.owner.typeConstructor, nme.SELF).setFlag(ARTIFACT) + newSym.updateInfo(newSym.info match { + case mt @ MethodType(params, res) => copyMethodType(mt, selfParamSym :: params, res) + }) + val selfParam = ValDef(selfParamSym) + val rhs = orig.rhs.substituteThis(newSym.owner, gen.mkAttributedIdent(selfParamSym)) // SD-186 intentionally leaving Ident($this) is unpositioned + .substituteSymbols(origParams, newSym.info.params.drop(1)).changeOwner(origSym -> newSym) + treeCopy.DefDef(orig, orig.mods, orig.name, orig.tparams, (selfParam :: orig.vparamss.head) :: Nil, orig.tpt, rhs).setSymbol(newSym) + } + + def expandFunction(localTyper: analyzer.Typer)(fun: Function, inConstructorFlag: Long): Tree = { + val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) + val parents = if (isFunctionType(fun.tpe)) { + anonClass addAnnotation SerialVersionUIDAnnotation + addSerializable(abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst)) + } else { + if (fun.tpe.typeSymbol.isSubClass(JavaSerializableClass)) + anonClass addAnnotation SerialVersionUIDAnnotation + fun.tpe :: Nil + } + anonClass setInfo ClassInfoType(parents, newScope, anonClass) - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - methDef.tpt setType localTyper.packedType(fun.body, methSym).deconst - methDef + // The original owner is used in the backend for the EnclosingMethod attribute. If fun is + // nested in a value-class method, its owner was already changed to the extension method. + // Saving the original owner allows getting the source structure from the class symbol. + defineOriginalOwner(anonClass, fun.symbol.originalOwner) + + val samDef = mkMethodFromFunction(localTyper)(anonClass, fun) + anonClass.info.decls enter samDef.symbol + + localTyper.typedPos(fun.pos) { + Block( + ClassDef(anonClass, NoMods, ListOfNil, List(samDef), fun.pos), + Typed(New(anonClass.tpe), TypeTree(fun.tpe))) + } } + + override def isPatVarWarnable = settings.warnUnusedPatVars } diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala index 689e6405d0..b78c5acc4f 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -97,4 +97,12 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo { case DocDef(_, definition) => isPureDef(definition) case _ => super.isPureDef(tree) } + + override def firstConstructor(stats: List[Tree]): Tree = { + def unwrap(stat: Tree): Tree = stat match { + case DocDef(_, defn) => unwrap(defn) + case tree => tree + } + super.firstConstructor(stats map unwrap) + } } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 934257092f..2d47e254e5 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -7,13 +7,6 @@ package scala.tools.nsc package ast import scala.reflect.ClassTag -import scala.reflect.internal.Flags.BYNAMEPARAM -import scala.reflect.internal.Flags.DEFAULTPARAM -import scala.reflect.internal.Flags.IMPLICIT -import scala.reflect.internal.Flags.PARAM -import scala.reflect.internal.Flags.PARAMACCESSOR -import scala.reflect.internal.Flags.PRESUPER -import scala.reflect.internal.Flags.TRAIT import scala.compat.Platform.EOL trait Trees extends scala.reflect.internal.Trees { self: Global => diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 52b8a51a79..46d533b037 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -12,7 +12,7 @@ import mutable.{ Buffer, ArrayBuffer, ListBuffer } import scala.util.control.ControlThrowable import scala.tools.nsc.util.CharArrayReader import scala.tools.nsc.ast.parser.xml.{MarkupParserCommon, Utility} -import scala.reflect.internal.Chars.{ SU, LF } +import scala.reflect.internal.Chars.SU // XXX/Note: many/most of the functions in here are almost direct cut and pastes // from another file - scala.xml.parsing.MarkupParser, it looks like. @@ -261,7 +261,7 @@ trait MarkupParsers { def coalesce(): ArrayBuffer[Tree] = { def copy() = { val buf = new ArrayBuffer[Tree] - var acc = new StringBuilder + val acc = new StringBuilder var pos: Position = NoPosition def emit() = if (acc.nonEmpty) { appendText(pos, buf, acc.toString) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 308669256d..707fe15f91 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -9,10 +9,9 @@ package scala.tools.nsc package ast.parser -import scala.collection.{ mutable, immutable } -import mutable.{ ListBuffer, StringBuilder } +import scala.collection.mutable +import mutable.ListBuffer import scala.reflect.internal.{ Precedence, ModifierFlags => Flags } -import scala.reflect.internal.Chars.{ isScalaLetter } import scala.reflect.internal.util.{ SourceFile, Position, FreshNameCreator, ListOfNil } import Tokens._ @@ -40,7 +39,7 @@ trait ParsersCommon extends ScannersCommon { self => */ abstract class ParserCommon { val in: ScannerCommon - def deprecationWarning(off: Offset, msg: String): Unit + def deprecationWarning(off: Offset, msg: String, since: String): Unit def accept(token: Token): Int /** Methods inParensOrError and similar take a second argument which, should @@ -155,7 +154,7 @@ self => // suppress warnings; silent abort on errors def warning(offset: Offset, msg: String): Unit = () - def deprecationWarning(offset: Offset, msg: String): Unit = () + def deprecationWarning(offset: Offset, msg: String, since: String): Unit = () def syntaxError(offset: Offset, msg: String): Unit = throw new MalformedInput(offset, msg) def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg) @@ -207,8 +206,8 @@ self => override def warning(offset: Offset, msg: String): Unit = reporter.warning(o2p(offset), msg) - override def deprecationWarning(offset: Offset, msg: String): Unit = - currentRun.reporting.deprecationWarning(o2p(offset), msg) + override def deprecationWarning(offset: Offset, msg: String, since: String): Unit = + currentRun.reporting.deprecationWarning(o2p(offset), msg, since) private var smartParsing = false @inline private def withSmartParsing[T](body: => T): T = { @@ -235,7 +234,7 @@ self => else currentRun.parsing.incompleteInputError(o2p(offset), msg) } - /** parse unit. If there are inbalanced braces, + /** parse unit. If there are unbalanced braces, * try to correct them and reparse. */ def smartParse(): Tree = withSmartParsing { @@ -685,6 +684,15 @@ self => } def isLiteral = isLiteralToken(in.token) + def isSimpleExprIntroToken(token: Token): Boolean = isLiteralToken(token) || (token match { + case IDENTIFIER | BACKQUOTED_IDENT | + THIS | SUPER | NEW | USCORE | + LPAREN | LBRACE | XMLSTART => true + case _ => false + }) + + def isSimpleExprIntro: Boolean = isExprIntroToken(in.token) + def isExprIntroToken(token: Token): Boolean = isLiteralToken(token) || (token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | @@ -804,7 +812,7 @@ self => false } else true - /** Strip the artifitial `Parens` node to create a tuple term Tree. */ + /** Strip the artificial `Parens` node to create a tuple term Tree. */ def stripParens(t: Tree) = t match { case Parens(ts) => atPos(t.pos) { makeSafeTupleTerm(ts, t.pos.point) } case _ => t @@ -1255,8 +1263,8 @@ self => case CHARLIT => in.charVal case INTLIT => in.intVal(isNegated).toInt case LONGLIT => in.intVal(isNegated) - case FLOATLIT => in.floatVal(isNegated).toFloat - case DOUBLELIT => in.floatVal(isNegated) + case FLOATLIT => in.floatVal(isNegated) + case DOUBLELIT => in.doubleVal(isNegated) case STRINGLIT | STRINGPART => in.strVal.intern() case TRUE => true case FALSE => false @@ -1636,11 +1644,14 @@ self => def prefixExpr(): Tree = { if (isUnaryOp) { atPos(in.offset) { - val name = nme.toUnaryName(rawIdent().toTermName) - if (name == nme.UNARY_- && isNumericLit) - simpleExprRest(literal(isNegated = true), canApply = true) - else - Select(stripParens(simpleExpr()), name) + if (lookingAhead(isSimpleExprIntro)) { + val uname = nme.toUnaryName(rawIdent().toTermName) + if (uname == nme.UNARY_- && isNumericLit) + simpleExprRest(literal(isNegated = true), canApply = true) + else + Select(stripParens(simpleExpr()), uname) + } + else simpleExpr() } } else simpleExpr() @@ -1722,9 +1733,7 @@ self => } simpleExprRest(app, canApply = true) case USCORE => - atPos(t.pos.start, in.skipToken()) { - Typed(stripParens(t), Function(Nil, EmptyTree)) - } + atPos(t.pos.start, in.skipToken()) { makeMethodValue(stripParens(t)) } case _ => t } @@ -1833,7 +1842,7 @@ self => val hasEq = in.token == EQUALS if (hasVal) { - if (hasEq) deprecationWarning(in.offset, "val keyword in for comprehension is deprecated") + if (hasEq) deprecationWarning(in.offset, "val keyword in for comprehension is deprecated", "2.10.0") else syntaxError(in.offset, "val in for comprehension must be followed by assignment") } @@ -1912,19 +1921,20 @@ self => } /** {{{ - * Pattern1 ::= varid `:' TypePat + * Pattern1 ::= boundvarid `:' TypePat * | `_' `:' TypePat * | Pattern2 - * SeqPattern1 ::= varid `:' TypePat + * SeqPattern1 ::= boundvarid `:' TypePat * | `_' `:' TypePat * | [SeqPattern2] * }}} */ def pattern1(): Tree = pattern2() match { case p @ Ident(name) if in.token == COLON => - if (treeInfo.isVarPattern(p)) + if (nme.isVariableName(name)) { + p.removeAttachment[BackquotedIdentifierAttachment.type] atPos(p.pos.start, in.skipToken())(Typed(p, compoundType())) - else { + } else { syntaxError(in.offset, "Pattern variables must start with a lower-case letter. (SLS 8.1.1.)") p } @@ -1932,25 +1942,27 @@ self => } /** {{{ - * Pattern2 ::= varid [ @ Pattern3 ] + * Pattern2 ::= id @ Pattern3 + * | `_' @ Pattern3 * | Pattern3 - * SeqPattern2 ::= varid [ @ SeqPattern3 ] - * | SeqPattern3 * }}} */ - def pattern2(): Tree = { - val p = pattern3() - - if (in.token != AT) p - else p match { - case Ident(nme.WILDCARD) => - in.nextToken() - pattern3() - case Ident(name) if treeInfo.isVarPattern(p) => - in.nextToken() - atPos(p.pos.start) { Bind(name, pattern3()) } - case _ => p - } + def pattern2(): Tree = (pattern3(), in.token) match { + case (Ident(nme.WILDCARD), AT) => + in.nextToken() + pattern3() + case (p @ Ident(name), AT) => + in.nextToken() + val body = pattern3() + atPos(p.pos.start, p.pos.start, body.pos.end) { + val t = Bind(name, body) + body match { + case Ident(nme.WILDCARD) => t updateAttachment AtBoundIdentifierAttachment + case _ if !settings.warnUnusedPatVars => t updateAttachment AtBoundIdentifierAttachment + case _ => t + } + } + case (p, _) => p } /** {{{ @@ -1975,8 +1987,8 @@ self => case _ => EmptyTree } def loop(top: Tree): Tree = reducePatternStack(base, top) match { - case next if isIdentExcept(raw.BAR) => pushOpInfo(next) ; loop(simplePattern(badPattern3)) - case next => next + case next if isIdent && !isRawBar => pushOpInfo(next) ; loop(simplePattern(badPattern3)) + case next => next } checkWildStar orElse stripParens(loop(top)) } @@ -2227,31 +2239,57 @@ self => * }}} */ def paramClauses(owner: Name, contextBounds: List[Tree], ofCaseClass: Boolean): List[List[ValDef]] = { - var implicitmod = 0 - var caseParam = ofCaseClass - def paramClause(): List[ValDef] = { - if (in.token == RPAREN) - return Nil - - if (in.token == IMPLICIT) { - in.nextToken() - implicitmod = Flags.IMPLICIT - } - commaSeparated(param(owner, implicitmod, caseParam )) - } - val vds = new ListBuffer[List[ValDef]] + var implicitSection = -1 + var implicitOffset = -1 + var warnAt = -1 + var caseParam = ofCaseClass + val vds = new ListBuffer[List[ValDef]] val start = in.offset + def paramClause(): List[ValDef] = if (in.token == RPAREN) Nil else { + val implicitmod = + if (in.token == IMPLICIT) { + if (implicitOffset == -1) { implicitOffset = in.offset ; implicitSection = vds.length } + else if (warnAt == -1) warnAt = in.offset + in.nextToken() + Flags.IMPLICIT + } else 0 + commaSeparated(param(owner, implicitmod, caseParam)) + } newLineOptWhenFollowedBy(LPAREN) - if (ofCaseClass && in.token != LPAREN) - syntaxError(in.lastOffset, "case classes without a parameter list are not allowed;\n"+ - "use either case objects or case classes with an explicit `()' as a parameter list.") - while (implicitmod == 0 && in.token == LPAREN) { + while (in.token == LPAREN) { in.nextToken() vds += paramClause() accept(RPAREN) caseParam = false newLineOptWhenFollowedBy(LPAREN) } + if (ofCaseClass) { + if (vds.isEmpty) + syntaxError(start, s"case classes must have a parameter list; try 'case class ${owner.encoded + }()' or 'case object ${owner.encoded}'") + else if (vds.head.nonEmpty && vds.head.head.mods.isImplicit) { + if (settings.isScala213) + syntaxError(start, s"case classes must have a non-implicit parameter list; try 'case class ${ + owner.encoded}()${ vds.map(vs => "(...)").mkString }'") + else { + deprecationWarning(start, s"case classes should have a non-implicit parameter list; adapting to 'case class ${ + owner.encoded}()${ vds.map(vs => "(...)").mkString }'", "2.12.2") + vds.insert(0, List.empty[ValDef]) + vds(1) = vds(1).map(vd => copyValDef(vd)(mods = vd.mods & ~Flags.CASEACCESSOR)) + if (implicitSection != -1) implicitSection += 1 + } + } + } + if (implicitSection != -1 && implicitSection != vds.length - 1) + syntaxError(implicitOffset, "an implicit parameter section must be last") + if (warnAt != -1) + syntaxError(warnAt, "multiple implicit parameter sections are not allowed") + else if (settings.warnExtraImplicit) { + // guard against anomalous class C(private implicit val x: Int)(implicit s: String) + val ttl = vds.count { case ValDef(mods, _, _, _) :: _ => mods.isImplicit ; case _ => false } + if (ttl > 1) + warning(in.offset, s"$ttl parameter sections are effectively implicit") + } val result = vds.toList if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods.isImplicit)))) { in.token match { @@ -2369,7 +2407,7 @@ self => while (in.token == VIEWBOUND) { val msg = "Use an implicit parameter instead.\nExample: Instead of `def f[A <% Int](a: A)` use `def f[A](a: A)(implicit ev: A => Int)`." if (settings.future) - deprecationWarning(in.offset, s"View bounds are deprecated. $msg") + deprecationWarning(in.offset, s"View bounds are deprecated. $msg", "2.12.0") contextBoundBuf += atPos(in.skipToken())(makeFunctionTypeTree(List(Ident(pname)), typ())) } while (in.token == COLON) { @@ -2663,14 +2701,14 @@ self => if (isStatSep || in.token == RBRACE) { if (restype.isEmpty) { if (settings.future) - deprecationWarning(in.lastOffset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit`.") + deprecationWarning(in.lastOffset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit`.", "2.12.0") restype = scalaUnitConstr } newmods |= Flags.DEFERRED EmptyTree } else if (restype.isEmpty && in.token == LBRACE) { if (settings.future) - deprecationWarning(in.offset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit =`.") + deprecationWarning(in.offset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit =`.", "2.12.0") restype = scalaUnitConstr blockExpr() } else { @@ -2819,14 +2857,8 @@ self => val (constrMods, vparamss) = if (mods.isTrait) (Modifiers(Flags.TRAIT), List()) else (accessModifierOpt(), paramClauses(name, classContextBounds, ofCaseClass = mods.isCase)) - var mods1 = mods - if (mods.isTrait) { - if (settings.YvirtClasses && in.token == SUBTYPE) mods1 |= Flags.DEFERRED - } else if (in.token == SUBTYPE) { - syntaxError("classes are not allowed to be virtual", skipIt = false) - } - val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart) - val result = gen.mkClassDef(mods1, name, tparams, template) + val template = templateOpt(mods, name, constrMods withAnnotations constrAnnots, vparamss, tstart) + val result = gen.mkClassDef(mods, name, tparams, template) // Context bounds generate implicit parameters (part of the template) with types // from tparams: we need to ensure these don't overlap if (!classContextBounds.isEmpty) @@ -2937,7 +2969,7 @@ self => case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => copyValDef(vdef)(mods = mods | Flags.PRESUPER) case tdef @ TypeDef(mods, name, tparams, rhs) => - deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.") + deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.", "2.11.0") treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs) case docdef @ DocDef(comm, rhs) => treeCopy.DocDef(docdef, comm, rhs) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index cd41c75298..0618f5d06e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -10,7 +10,7 @@ import scala.reflect.internal.util._ import scala.reflect.internal.Chars._ import Tokens._ import scala.annotation.{ switch, tailrec } -import scala.collection.{ mutable, immutable } +import scala.collection.mutable import mutable.{ ListBuffer, ArrayBuffer } import scala.tools.nsc.ast.parser.xml.Utility.isNameStart import scala.language.postfixOps @@ -35,7 +35,28 @@ trait ScannersCommon { // things to fill in, in addition to buf, decodeUni which come from CharArrayReader def error(off: Offset, msg: String): Unit def incompleteInputError(off: Offset, msg: String): Unit - def deprecationWarning(off: Offset, msg: String): Unit + def deprecationWarning(off: Offset, msg: String, since: String): Unit + } + + // Hooks for ScaladocUnitScanner and ScaladocJavaUnitScanner + trait DocScanner { + protected def beginDocComment(prefix: String): Unit = {} + protected def processCommentChar(): Unit = {} + protected def finishDocComment(): Unit = {} + + private var lastDoc: DocComment = null + // get last doc comment + def flushDoc(): DocComment = try lastDoc finally lastDoc = null + def registerDocComment(raw: String, pos: Position) = { + lastDoc = DocComment(raw, pos) + signalParsedDocComment(raw, pos) + } + + /** To prevent doc comments attached to expressions from leaking out of scope + * onto the next documentable entity, they are discarded upon passing a right + * brace, bracket, or parenthesis. + */ + def discardDocBuffer(): Unit = {} } def createKeywordArray(keywords: Seq[(Name, Token)], defaultToken: Token): (Token, Array[Token]) = { @@ -103,11 +124,11 @@ trait Scanners extends ScannersCommon { } } - abstract class Scanner extends CharArrayReader with TokenData with ScannerData with ScannerCommon { + abstract class Scanner extends CharArrayReader with TokenData with ScannerData with ScannerCommon with DocScanner { private def isDigit(c: Char) = java.lang.Character isDigit c private var openComments = 0 - protected def putCommentChar(): Unit = nextChar() + final protected def putCommentChar(): Unit = { processCommentChar(); nextChar() } @tailrec private def skipLineComment(): Unit = ch match { case SU | CR | LF => @@ -134,8 +155,6 @@ trait Scanners extends ScannersCommon { case SU => incompleteInputError("unclosed comment") case _ => putCommentChar() ; skipNestedComments() } - def skipDocComment(): Unit = skipNestedComments() - def skipBlockComment(): Unit = skipNestedComments() private def skipToCommentEnd(isLineComment: Boolean): Unit = { nextChar() @@ -147,27 +166,23 @@ trait Scanners extends ScannersCommon { // Check for the amazing corner case of /**/ if (ch == '/') nextChar() - else - skipDocComment() + else { + beginDocComment("/**") + skipNestedComments() + } } - else skipBlockComment() + else skipNestedComments() } } /** @pre ch == '/' * Returns true if a comment was skipped. */ - def skipComment(): Boolean = ch match { - case '/' | '*' => skipToCommentEnd(isLineComment = ch == '/') ; true + final def skipComment(): Boolean = ch match { + case '/' | '*' => skipToCommentEnd(isLineComment = ch == '/') ; finishDocComment(); true case _ => false } - def flushDoc(): DocComment = null - /** To prevent doc comments attached to expressions from leaking out of scope - * onto the next documentable entity, they are discarded upon passing a right - * brace, bracket, or parenthesis. - */ - def discardDocBuffer(): Unit = () def isAtEnd = charOffset >= buf.length @@ -208,7 +223,7 @@ trait Scanners extends ScannersCommon { if (name == nme.MACROkw) syntaxError(s"$name is now a reserved word; usage as an identifier is disallowed") else if (emitIdentifierDeprecationWarnings) - deprecationWarning(s"$name is now a reserved word; usage as an identifier is deprecated") + deprecationWarning(s"$name is a reserved word (since 2.10.0); usage as an identifier is deprecated", "2.10.0") } } } @@ -246,6 +261,14 @@ trait Scanners extends ScannersCommon { private def inMultiLineInterpolation = inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART + /** Are we in a `${ }` block? such that RBRACE exits back into multiline string. */ + private def inMultiLineInterpolatedExpression = { + sepRegions match { + case RBRACE :: STRINGLIT :: STRINGPART :: rest => true + case _ => false + } + } + /** read next token and return last offset */ def skipToken(): Offset = { @@ -312,7 +335,7 @@ trait Scanners extends ScannersCommon { lastOffset -= 1 } if (inStringInterpolation) fetchStringPart() else fetchToken() - if(token == ERROR) { + if (token == ERROR) { if (inMultiLineInterpolation) sepRegions = sepRegions.tail.tail else if (inStringInterpolation) @@ -363,6 +386,17 @@ trait Scanners extends ScannersCommon { next copyFrom this this copyFrom prev } + } else if (token == COMMA) { + // SIP-27 Trailing Comma (multi-line only) support + // If a comma is followed by a new line & then a closing paren, bracket or brace + // then it is a trailing comma and is ignored + val saved = new ScannerData {} copyFrom this + fetchToken() + if (afterLineEnd() && (token == RPAREN || token == RBRACKET || token == RBRACE)) { + /* skip the trailing comma */ + } else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*," + /* skip the trailing comma */ + } else this copyFrom saved } // print("["+this+"]") @@ -515,7 +549,7 @@ trait Scanners extends ScannersCommon { charLitOr(getIdentRest) else if (isOperatorPart(ch) && (ch != '\\')) charLitOr(getOperatorRest) - else { + else if (!isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape)) { getLitChar() if (ch == '\'') { nextChar() @@ -525,6 +559,8 @@ trait Scanners extends ScannersCommon { syntaxError("unclosed character literal") } } + else + syntaxError("unclosed character literal") } fetchSingleQuote() case '.' => @@ -545,7 +581,8 @@ trait Scanners extends ScannersCommon { case ')' => nextChar(); token = RPAREN case '}' => - nextChar(); token = RBRACE + if (inMultiLineInterpolatedExpression) nextRawChar() else nextChar() + token = RBRACE case '[' => nextChar(); token = LBRACKET case ']' => @@ -690,7 +727,7 @@ trait Scanners extends ScannersCommon { private def unclosedStringLit(): Unit = syntaxError("unclosed string literal") - private def getRawStringLit(): Unit = { + @tailrec private def getRawStringLit(): Unit = { if (ch == '\"') { nextRawChar() if (isTripleQuote()) { @@ -707,7 +744,7 @@ trait Scanners extends ScannersCommon { } } - @scala.annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = { + @tailrec private def getStringPart(multiLine: Boolean): Unit = { def finishStringPart() = { setStrVal() token = STRINGPART @@ -822,7 +859,7 @@ trait Scanners extends ScannersCommon { if (settings.future) syntaxError(start, msg("unsupported")) else - deprecationWarning(start, msg("deprecated")) + deprecationWarning(start, msg("deprecated"), "2.11.0") putChar(oct.toChar) } else { ch match { @@ -946,23 +983,45 @@ trait Scanners extends ScannersCommon { def intVal: Long = intVal(negated = false) - /** Convert current strVal, base to double value + private val zeroFloat = raw"[0.]+(?:[eE][+-]?[0-9]+)?[fFdD]?".r + + /** Convert current strVal, base to float value. */ - def floatVal(negated: Boolean): Double = { - val limit: Double = if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue + def floatVal(negated: Boolean): Float = { try { - val value: Double = java.lang.Double.valueOf(strVal).doubleValue() - if (value > limit) + val value: Float = java.lang.Float.parseFloat(strVal) + if (value > Float.MaxValue) syntaxError("floating point number too large") + if (value == 0.0f && !zeroFloat.pattern.matcher(strVal).matches) + syntaxError("floating point number too small") if (negated) -value else value } catch { case _: NumberFormatException => syntaxError("malformed floating point number") + 0.0f + } + } + + def floatVal: Float = floatVal(negated = false) + + /** Convert current strVal, base to double value. + */ + def doubleVal(negated: Boolean): Double = { + try { + val value: Double = java.lang.Double.parseDouble(strVal) + if (value > Double.MaxValue) + syntaxError("double precision floating point number too large") + if (value == 0.0d && !zeroFloat.pattern.matcher(strVal).matches) + syntaxError("double precision floating point number too small") + if (negated) -value else value + } catch { + case _: NumberFormatException => + syntaxError("malformed double precision floating point number") 0.0 } } - def floatVal: Double = floatVal(negated = false) + def doubleVal: Double = doubleVal(negated = false) def checkNoLetter(): Unit = { if (isIdentifierPart(ch) && ch >= ' ') @@ -1032,7 +1091,7 @@ trait Scanners extends ScannersCommon { /** generate an error at the current token offset */ def syntaxError(msg: String): Unit = syntaxError(offset, msg) - def deprecationWarning(msg: String): Unit = deprecationWarning(offset, msg) + def deprecationWarning(msg: String, since: String): Unit = deprecationWarning(offset, msg, since) /** signal an error where the input ended in the middle of a token */ def incompleteInputError(msg: String): Unit = { @@ -1195,15 +1254,15 @@ trait Scanners extends ScannersCommon { class MalformedInput(val offset: Offset, val msg: String) extends Exception /** A scanner for a given source file not necessarily attached to a compilation unit. - * Useful for looking inside source files that aren not currently compiled to see what's there + * Useful for looking inside source files that are not currently compiled to see what's there */ class SourceFileScanner(val source: SourceFile) extends Scanner { val buf = source.content override val decodeUni: Boolean = !settings.nouescape // suppress warnings, throw exception on errors - def deprecationWarning(off: Offset, msg: String): Unit = () - def error (off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) + def deprecationWarning(off: Offset, msg: String, since: String): Unit = () + def error(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) def incompleteInputError(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) } @@ -1212,9 +1271,9 @@ trait Scanners extends ScannersCommon { class UnitScanner(val unit: CompilationUnit, patches: List[BracePatch]) extends SourceFileScanner(unit.source) { def this(unit: CompilationUnit) = this(unit, List()) - override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg) - override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg) - override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg) + override def deprecationWarning(off: Offset, msg: String, since: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg, since) + override def error(off: Offset, msg: String) = reporter.error(unit.position(off), msg) + override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg) private var bracePatches: List[BracePatch] = patches diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala index 67241ef639..c3c3ee9d47 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package ast.parser -import scala.collection.{ mutable, immutable } +import scala.collection.mutable import symtab.Flags.MUTABLE import scala.reflect.internal.util.ListOfNil import scala.reflect.internal.util.StringOps.splitWhere diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index df2073785b..e0667b5a3e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -82,7 +82,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse } private def initialUnitBody(unit: CompilationUnit): Tree = { - if (unit.isJava) new JavaUnitParser(unit).parse() + if (unit.isJava) newJavaUnitParser(unit).parse() else if (currentRun.parsing.incompleteHandled) newUnitParser(unit).parse() else newUnitParser(unit).smartParse() } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 45f731686a..7866fcf2dc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package ast.parser import symtab.Flags._ -import scala.collection.mutable.ListBuffer import scala.reflect.internal.util.{Position, SourceFile, FreshNameCreator} /** Methods for building trees, used in the parser. All the trees @@ -36,6 +35,9 @@ abstract class TreeBuilder { def repeatedApplication(tpe: Tree): Tree = AppliedTypeTree(rootScalaDot(tpnme.REPEATED_PARAM_CLASS_NAME), List(tpe)) + // represents `expr _`, as specified in Method Values of spec/06-expressions.md + def makeMethodValue(expr: Tree): Tree = Typed(expr, Function(Nil, EmptyTree)) + def makeImportSelector(name: Name, nameOffset: Int): ImportSelector = ImportSelector(name, nameOffset, name, nameOffset) |