Skip to content

Record syntactic information about modifiers #1608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,51 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
class InfixOpBlock(leftOperand: Tree, rightOp: Tree) extends Block(leftOperand :: Nil, rightOp)

// ----- Modifiers -----------------------------------------------------
/** Mod is intended to record syntactic information about modifiers, it's
* NOT a replacement of FlagSet.
*
* For any query about semantic information, check `flags` instead.
*/
sealed abstract class Mod(val flags: FlagSet) extends Positioned

/** Modifiers and annotations for definitions
* @param flags The set flags
object Mod {
case class Private() extends Mod(Flags.Private)

case class Protected() extends Mod(Flags.Protected)

case class Val() extends Mod(Flags.EmptyFlags)

case class Var() extends Mod(Flags.Mutable)

case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag)

case class Final() extends Mod(Flags.Final)

case class Sealed() extends Mod(Flags.Sealed)

case class Override() extends Mod(Flags.Override)

case class Abstract() extends Mod(Flags.Abstract)

case class Lazy() extends Mod(Flags.Lazy)

case class Inline() extends Mod(Flags.Inline)

case class Type() extends Mod(Flags.EmptyFlags)
}

/** Modifiers and annotations for definitions
*
* @param flags The set flags
* @param privateWithin If a private or protected has is followed by a
* qualifier [q], the name q, "" as a typename otherwise.
* @param annotations The annotations preceding the modifiers
*/
case class Modifiers (
flags: FlagSet = EmptyFlags,
privateWithin: TypeName = tpnme.EMPTY,
annotations: List[Tree] = Nil) extends Positioned with Cloneable {
annotations: List[Tree] = Nil,
mods: List[Mod] = Nil) extends Positioned with Cloneable {

def is(fs: FlagSet): Boolean = flags is fs
def is(fc: FlagConjunction): Boolean = flags is fc
Expand All @@ -120,7 +154,15 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
if (this.flags == flags) this
else copy(flags = flags)

def withAddedAnnotation(annot: Tree): Modifiers =
def withAddedMod(mod: Mod): Modifiers =
if (mods.exists(_ eq mod)) this
else withMods(mods :+ mod)

def withMods(ms: List[Mod]): Modifiers =
if (mods eq ms) this
else copy(mods = ms)

def withAddedAnnotation(annot: Tree): Modifiers =
if (annotations.exists(_ eq annot)) this
else withAnnotations(annotations :+ annot)

Expand Down
89 changes: 52 additions & 37 deletions src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1097,8 +1097,9 @@ object Parsers {
/** Expr ::= implicit Id `=>' Expr
* BlockResult ::= implicit Id [`:' InfixType] `=>' Block
*/
def implicitClosure(start: Int, location: Location.Value): Tree = {
val mods = atPos(start) { Modifiers(Implicit) }
def implicitClosure(start: Int, location: Location.Value, implicitMod: Option[Mod] = None): Tree = {
var mods = atPos(start) { Modifiers(Implicit) }
if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get)
val id = termIdent()
val paramExpr =
if (location == Location.InBlock && in.token == COLON)
Expand Down Expand Up @@ -1464,19 +1465,19 @@ object Parsers {

/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */

private def flagOfToken(tok: Int): FlagSet = tok match {
case ABSTRACT => Abstract
case FINAL => Final
case IMPLICIT => ImplicitCommon
case INLINE => Inline
case LAZY => Lazy
case OVERRIDE => Override
case PRIVATE => Private
case PROTECTED => Protected
case SEALED => Sealed
private def modOfToken(tok: Int): Mod = tok match {
case ABSTRACT => Mod.Abstract()
case FINAL => Mod.Final()
case IMPLICIT => Mod.Implicit(ImplicitCommon)
case INLINE => Mod.Inline()
case LAZY => Mod.Lazy()
case OVERRIDE => Mod.Override()
case PRIVATE => Mod.Private()
case PROTECTED => Mod.Protected()
case SEALED => Mod.Sealed()
}

/** Drop `private' modifier when followed by a qualifier.
/** Drop `private' modifier when followed by a qualifier.
* Contract `abstract' and `override' to ABSOVERRIDE
*/
private def normalize(mods: Modifiers): Modifiers =
Expand All @@ -1488,11 +1489,11 @@ object Parsers {
mods

private def addModifier(mods: Modifiers): Modifiers = {
val flag = flagOfToken(in.token)
if (mods is flag) syntaxError(RepeatedModifier(flag.toString))
val res = addFlag(mods, flag)
in.nextToken()
res
val tok = in.token
val mod = atPos(in.skipToken()) { modOfToken(tok) }

if (mods is mod.flags) syntaxError(RepeatedModifier(mod.flags.toString))
addMod(mods, mod)
}

private def compatible(flags1: FlagSet, flags2: FlagSet): Boolean = (
Expand All @@ -1518,6 +1519,11 @@ object Parsers {
}
}

/** Always add the syntactic `mod`, but check and conditionally add semantic `mod.flags`
*/
def addMod(mods: Modifiers, mod: Mod): Modifiers =
addFlag(mods, mod.flags).withAddedMod(mod)

/** AccessQualifier ::= "[" (Id | this) "]"
*/
def accessQualifierOpt(mods: Modifiers): Modifiers =
Expand Down Expand Up @@ -1614,8 +1620,8 @@ object Parsers {
mods =
atPos(start, in.offset) {
if (in.token == TYPE) {
in.nextToken()
mods | Param | ParamAccessor
val mod = atPos(in.skipToken()) { Mod.Type() }
(mods | Param | ParamAccessor).withAddedMod(mod)
} else {
if (mods.hasFlags) syntaxError("`type' expected")
mods | Param | PrivateLocal
Expand Down Expand Up @@ -1659,7 +1665,7 @@ object Parsers {
* Param ::= id `:' ParamType [`=' Expr]
*/
def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = {
var implicitFlag = EmptyFlags
var implicitMod: Mod = null
var firstClauseOfCaseClass = ofCaseClass
var implicitOffset = -1 // use once
def param(): ValDef = {
Expand All @@ -1670,11 +1676,11 @@ object Parsers {
mods =
atPos(start, in.offset) {
if (in.token == VAL) {
in.nextToken()
mods
val mod = atPos(in.skipToken()) { Mod.Val() }
mods.withAddedMod(mod)
} else if (in.token == VAR) {
in.nextToken()
addFlag(mods, Mutable)
val mod = atPos(in.skipToken()) { Mod.Var() }
addMod(mods, mod)
} else {
if (!(mods.flags &~ (ParamAccessor | Inline)).isEmpty)
syntaxError("`val' or `var' expected")
Expand All @@ -1696,7 +1702,7 @@ object Parsers {
if (in.token == ARROW) {
if (owner.isTypeName && !(mods is Local))
syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name")
else if (!implicitFlag.isEmpty)
else if (implicitMod != null)
syntaxError("implicit parameters may not be call-by-name")
}
paramType()
Expand All @@ -1708,15 +1714,16 @@ object Parsers {
mods = mods.withPos(mods.pos.union(Position(implicitOffset, implicitOffset)))
implicitOffset = -1
}
ValDef(name, tpt, default).withMods(addFlag(mods, implicitFlag))
if (implicitMod != null) mods = addMod(mods, implicitMod)
ValDef(name, tpt, default).withMods(mods)
}
}
def paramClause(): List[ValDef] = inParens {
if (in.token == RPAREN) Nil
else {
if (in.token == IMPLICIT) {
implicitOffset = in.skipToken()
implicitFlag = Implicit
implicitOffset = in.offset
implicitMod = atPos(in.skipToken()) { Mod.Implicit(Implicit) }
}
commaSeparated(param)
}
Expand All @@ -1726,7 +1733,7 @@ object Parsers {
if (in.token == LPAREN)
paramClause() :: {
firstClauseOfCaseClass = false
if (implicitFlag.isEmpty) clauses() else Nil
if (implicitMod == null) clauses() else Nil
}
else Nil
}
Expand Down Expand Up @@ -1819,9 +1826,13 @@ object Parsers {
*/
def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match {
case VAL =>
patDefOrDcl(start, posMods(start, mods), in.getDocComment(start))
val mod = atPos(in.skipToken()) { Mod.Val() }
val mods1 = mods.withAddedMod(mod)
patDefOrDcl(start, mods1, in.getDocComment(start))
case VAR =>
patDefOrDcl(start, posMods(start, addFlag(mods, Mutable)), in.getDocComment(start))
val mod = atPos(in.skipToken()) { Mod.Var() }
val mod1 = addMod(mods, mod)
patDefOrDcl(start, mod1, in.getDocComment(start))
case DEF =>
defDefOrDcl(start, posMods(start, mods), in.getDocComment(start))
case TYPE =>
Expand Down Expand Up @@ -2184,8 +2195,11 @@ object Parsers {
stats.toList
}

def localDef(start: Int, implicitFlag: FlagSet): Tree =
defOrDcl(start, addFlag(defAnnotsMods(localModifierTokens), implicitFlag))
def localDef(start: Int, implicitFlag: FlagSet, implicitMod: Option[Mod] = None): Tree = {
var mods = addFlag(defAnnotsMods(localModifierTokens), implicitFlag)
if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get)
defOrDcl(start, mods)
}

/** BlockStatSeq ::= { BlockStat semi } [ResultExpr]
* BlockStat ::= Import
Expand All @@ -2205,9 +2219,10 @@ object Parsers {
stats += expr(Location.InBlock)
else if (isDefIntro(localModifierTokens))
if (in.token == IMPLICIT) {
val start = in.skipToken()
if (isIdent) stats += implicitClosure(start, Location.InBlock)
else stats += localDef(start, ImplicitCommon)
val start = in.offset
val mod = atPos(in.skipToken()) { Mod.Implicit(ImplicitCommon) }
if (isIdent) stats += implicitClosure(start, Location.InBlock, Some(mod))
else stats += localDef(start, ImplicitCommon, Some(mod))
} else {
stats += localDef(in.offset, EmptyFlags)
}
Expand Down
Loading