Skip to content
Next Next commit
Accept more : at EOLs under -Yindent-colon
Systamtically accept a `:` as COLON when it cannot be confused with a `:` at EOL.
  • Loading branch information
odersky committed Mar 18, 2021
commit 1d07c1155e7a99a00200a20757fb2940ce2068f3
56 changes: 23 additions & 33 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ object Parsers {
offset
}

def acceptColon(): Int =
val offset = in.offset
if in.isColon() then { in.nextToken(); offset }
else accept(COLON)

/** semi = nl {nl} | `;'
* nl = `\n' // where allowed
*/
Expand Down Expand Up @@ -861,7 +866,7 @@ object Parsers {
lookahead.nextToken()
skipParams()
skipParams()
lookahead.token == COLON
lookahead.isColon()

def followingIsExtension() =
val next = in.lookahead.token
Expand Down Expand Up @@ -1378,7 +1383,7 @@ object Parsers {
if isErased then imods = addModifier(imods)
val paramStart = in.offset
val ts = funArgType() match {
case Ident(name) if name != tpnme.WILDCARD && in.token == COLON =>
case Ident(name) if name != tpnme.WILDCARD && in.isColon() =>
isValParamList = true
funArgTypesRest(
typedFunParam(paramStart, name.toTermName, imods),
Expand Down Expand Up @@ -1467,7 +1472,7 @@ object Parsers {
/** TypedFunParam ::= id ':' Type */
def typedFunParam(start: Offset, name: TermName, mods: Modifiers = EmptyModifiers): ValDef =
atSpan(start) {
accept(COLON)
acceptColon()
makeParameter(name, typ(), mods)
}

Expand Down Expand Up @@ -1761,24 +1766,23 @@ object Parsers {
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
}

def contextBounds(pname: TypeName): List[Tree] = in.token match {
case COLON =>
def contextBounds(pname: TypeName): List[Tree] =
if in.isColon() then
atSpan(in.skipToken()) {
AppliedTypeTree(toplevelTyp(), Ident(pname))
} :: contextBounds(pname)
case VIEWBOUND =>
else if in.token == VIEWBOUND then
report.errorOrMigrationWarning(
"view bounds `<%' are no longer supported, use a context bound `:' instead",
in.sourcePos())
atSpan(in.skipToken()) {
Function(Ident(pname) :: Nil, toplevelTyp())
} :: contextBounds(pname)
case _ =>
else
Nil
}

def typedOpt(): Tree =
if (in.token == COLON) { in.nextToken(); toplevelTyp() }
if in.isColon() then { in.nextToken(); toplevelTyp() }
else TypeTree().withSpan(Span(in.lastOffset))

def typeDependingOn(location: Location): Tree =
Expand Down Expand Up @@ -2195,6 +2199,7 @@ object Parsers {
* | SimpleExpr `.` MatchClause
* | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
* | SimpleExpr1 `:` nl ArgumentExprs
* Quoted ::= ‘'’ ‘{’ Block ‘}’
* | ‘'’ ‘[’ Type ‘]’
*/
Expand Down Expand Up @@ -2342,10 +2347,9 @@ object Parsers {
lookahead.nextToken()
if (lookahead.token == RPAREN)
!fn.isInstanceOf[Trees.Apply[?]] // allow one () as annotation argument
else if (lookahead.token == IDENTIFIER) {
else if lookahead.token == IDENTIFIER then
lookahead.nextToken()
lookahead.token != COLON
}
!lookahead.isColon()
else in.canStartExprTokens.contains(lookahead.token)
}
}
Expand Down Expand Up @@ -2762,7 +2766,7 @@ object Parsers {
if allowed.contains(in.token)
|| in.isSoftModifier
&& localModifierTokens.subsetOf(allowed) // soft modifiers are admissible everywhere local modifiers are
&& in.lookahead.token != COLON
&& !in.lookahead.isColon()
then
val isAccessMod = accessModifierTokens contains in.token
val mods1 = addModifier(mods)
Expand Down Expand Up @@ -2931,7 +2935,7 @@ object Parsers {
}
atSpan(start, nameStart) {
val name = ident()
accept(COLON)
acceptColon()
if (in.token == ARROW && ofClass && !mods.is(Local))
syntaxError(VarValParametersMayNotBeCallByName(name, mods.is(Mutable)))
val tpt = paramType()
Expand Down Expand Up @@ -2969,7 +2973,7 @@ object Parsers {
val isParams =
!impliedMods.is(Given)
|| startParamTokens.contains(in.token)
|| isIdent && (in.name == nme.inline || in.lookahead.token == COLON)
|| isIdent && (in.name == nme.inline || in.lookahead.isColon())
if isParams then commaSeparated(() => param())
else contextTypes(ofClass, nparams)
checkVarArgsRules(clause)
Expand Down Expand Up @@ -3191,13 +3195,7 @@ object Parsers {
case _ =>
first :: Nil
}
def emptyType = TypeTree().withSpan(Span(in.lastOffset))
val tpt =
if (in.token == COLON) {
in.nextToken()
toplevelTyp()
}
else emptyType
val tpt = typedOpt()
val rhs =
if tpt.isEmpty || in.token == EQUALS then
accept(EQUALS)
Expand Down Expand Up @@ -3276,15 +3274,7 @@ object Parsers {
var name = ident.name.asTermName
val tparams = typeParamClauseOpt(ParamOwner.Def)
val vparamss = paramClauses(numLeadParams = numLeadParams)
var tpt = fromWithinReturnType {
if in.token == COLONEOL then in.token = COLON
// a hack to allow
//
// def f():
// T
//
typedOpt()
}
var tpt = fromWithinReturnType { typedOpt() }
if (migrateTo3) newLineOptWhenFollowedBy(LBRACE)
val rhs =
if in.token == EQUALS then
Expand Down Expand Up @@ -3528,7 +3518,7 @@ object Parsers {
else Nil
newLinesOpt()
val noParams = tparams.isEmpty && vparamss.isEmpty
if !(name.isEmpty && noParams) then accept(COLON)
if !(name.isEmpty && noParams) then acceptColon()
val parents =
if isSimpleLiteral then rejectWildcardType(annotType()) :: Nil
else constrApp() :: withConstrApps()
Expand Down Expand Up @@ -3570,7 +3560,7 @@ object Parsers {
isUsingClause(extParams)
do ()
leadParamss ++= paramClauses(givenOnly = true, numLeadParams = nparams)
if in.token == COLON then
if in.isColon() then
syntaxError("no `:` expected here")
in.nextToken()
val methods =
Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ object Scanners {
def isNestedStart = token == LBRACE || token == INDENT
def isNestedEnd = token == RBRACE || token == OUTDENT

/** Is token a COLON, after having converted COLONEOL to COLON?
* The conversion means that indentation is not significant after `:`
* anymore. So, warning: this is a side-effecting operation.
*/
def isColon() =
if token == COLONEOL then token = COLON
token == COLON

/** Is current token first one after a newline? */
def isAfterLineEnd: Boolean = lineOffset >= 0

Expand Down