From 5951f69a74f5faeff1e19d52da08701786a3def5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 1 May 2020 12:13:30 +0200 Subject: [PATCH 1/4] Simplify treatment of single-token lookahead in Scanner No need to start a separate scanner; we can simply use the existing prev/next logic. --- .../dotty/tools/dotc/parsing/Parsers.scala | 20 ++++---- .../dotty/tools/dotc/parsing/Scanners.scala | 46 +++++++++---------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7c1b2c522895..50e8ad11d24d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -206,7 +206,9 @@ object Parsers { def isBindingIntro: Boolean = { in.token match { case USCORE => true - case IDENTIFIER | BACKQUOTED_IDENT => in.lookaheadIn(BitSet(ARROW, CTXARROW)) + case IDENTIFIER | BACKQUOTED_IDENT => + val nxt = in.lookahead.token + nxt == ARROW || nxt == CTXARROW case LPAREN => val lookahead = in.LookaheadScanner() lookahead.skipParens() @@ -235,7 +237,7 @@ object Parsers { */ def isSplice: Boolean = in.token == IDENTIFIER && in.name(0) == '$' && { - if (in.name.length == 1) in.lookaheadIn(BitSet(LBRACE)) + if in.name.length == 1 then in.lookahead.token == LBRACE else (staged & StageKind.Quoted) != 0 } @@ -1506,7 +1508,10 @@ object Parsers { /** Is current ident a `*`, and is it followed by a `)` or `,`? */ def isPostfixStar: Boolean = - in.name == nme.raw.STAR && in.lookaheadIn(BitSet(RPAREN, COMMA)) + in.name == nme.raw.STAR && { + val nxt = in.lookahead.token + nxt == RPAREN || nxt == COMMA + } def infixTypeRest(t: Tree): Tree = infixOps(t, canStartTypeTokens, refinedType, isType = true, isOperator = !isPostfixStar) @@ -1588,11 +1593,10 @@ object Parsers { else if (in.token == LBRACE) atSpan(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } else if (isSimpleLiteral) { SingletonTypeTree(literal(inType = true)) } - else if (isIdent(nme.raw.MINUS) && in.lookaheadIn(numericLitTokens)) { + else if isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token) then val start = in.offset in.nextToken() SingletonTypeTree(literal(negOffset = start, inType = true)) - } else if (in.token == USCORE) { if sourceVersion.isAtLeast(`3.1`) then deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead") @@ -2012,7 +2016,7 @@ object Parsers { case _ => if isIdent(nme.inline) && !in.inModifierPosition() - && in.lookaheadIn(in.canStartExprTokens) + && in.canStartExprTokens.contains(in.lookahead.token) then val start = in.skipToken() in.token match @@ -3005,7 +3009,7 @@ object Parsers { val isParams = !impliedMods.is(Given) || startParamTokens.contains(in.token) - || isIdent && (in.name == nme.inline || in.lookaheadIn(BitSet(COLON))) + || isIdent && (in.name == nme.inline || in.lookahead.token == COLON) if isParams then commaSeparated(() => param()) else contextTypes(ofClass, nparams) checkVarArgsRules(clause) @@ -3567,7 +3571,7 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Def) newLineOpt() val vparamss = - if in.token == LPAREN && in.lookaheadIn(nme.using) + if in.token == LPAREN && in.lookahead.isIdent(nme.using) then paramClauses(givenOnly = true) else Nil newLinesOpt() diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 85894324aac4..d818d43e7132 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -98,6 +98,13 @@ object Scanners { this.strVal = td.strVal this.base = td.base } + + def isNewLine = token == NEWLINE || token == NEWLINES + def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT + def isIdent(name: Name) = token == IDENTIFIER && this.name == name + + def isNestedStart = token == LBRACE || token == INDENT + def isNestedEnd = token == RBRACE || token == OUTDENT } abstract class ScannerCommon(source: SourceFile)(implicit ctx: Context) extends CharArrayReader with TokenData { @@ -535,7 +542,7 @@ object Scanners { def observeColonEOL(): Unit = if token == COLON then - lookahead() + lookAhead() val atEOL = isAfterLineEnd || token == EOF reset() if atEOL then token = COLONEOL @@ -562,7 +569,7 @@ object Scanners { insert(OUTDENT, offset) case _ => - def lookahead() = { + def lookAhead() = { prev.copyFrom(this) lastOffset = lastCharOffset fetchToken() @@ -591,12 +598,12 @@ object Scanners { } token match { case CASE => - lookahead() + lookAhead() if (token == CLASS) fuse(CASECLASS) else if (token == OBJECT) fuse(CASEOBJECT) else reset() case SEMI => - lookahead() + lookAhead() if (token != ELSE) reset() case COMMA => def isEnclosedInParens(r: Region): Boolean = r match @@ -608,7 +615,7 @@ object Scanners { insert(OUTDENT, offset) currentRegion = r.outer case _ => - lookahead() + lookAhead() if isAfterLineEnd && (token == RPAREN || token == RBRACKET || token == RBRACE || token == OUTDENT) then @@ -892,6 +899,15 @@ object Scanners { // Lookahead --------------------------------------------------------------- + /** The next token after this one. + * Newlines and indent/unindent tokens are skipped. + */ + def lookahead: TokenData = + if next.token == EMPTY then + lookAhead() + reset() + next + class LookaheadScanner() extends Scanner(source, offset) /** Skip matching pairs of `(...)` or `[...]` parentheses. @@ -904,17 +920,6 @@ object Scanners { if token == opening && multiple then skipParens() else nextToken() nextToken() - /** Is the token following the current one in `tokens`? */ - def lookaheadIn(follow: BitSet | TermName): Boolean = - val lookahead = LookaheadScanner() - while - lookahead.nextToken() - lookahead.isNewLine - do () - follow match - case tokens: BitSet => tokens.contains(lookahead.token) - case name: TermName => lookahead.token == IDENTIFIER && lookahead.name == name - /** Is the current token in a position where a modifier is allowed? */ def inModifierPosition(): Boolean = { val lookahead = LookaheadScanner() @@ -1010,14 +1015,7 @@ object Scanners { isSoftModifier && inModifierPosition() def isSoftModifierInParamModifierPosition: Boolean = - isSoftModifier && !lookaheadIn(BitSet(COLON)) - - def isNewLine = token == NEWLINE || token == NEWLINES - def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT - def isIdent(name: Name) = token == IDENTIFIER && this.name == name - - def isNestedStart = token == LBRACE || token == INDENT - def isNestedEnd = token == RBRACE || token == OUTDENT + isSoftModifier && lookahead.token != COLON def canStartStatTokens = if migrateTo3 then canStartStatTokens2 else canStartStatTokens3 From 5fb7c4edb9f9b9cc9d4f25a249447235c61ac7c9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 1 May 2020 14:14:23 +0200 Subject: [PATCH 2/4] Refactor handling of paths Instead of passing a closure down, use one-token lookahead going froward. This simplifies the logic of parsing paths. --- .../dotty/tools/dotc/parsing/Parsers.scala | 155 +++++++++--------- .../dotty/tools/dotc/parsing/Scanners.scala | 3 + .../dotty/tools/repl/TabcompleteTests.scala | 2 +- 3 files changed, 82 insertions(+), 78 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 50e8ad11d24d..4df5ebe9404a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1045,6 +1045,9 @@ object Parsers { if in.token == MATCH then matchClause(t) else Select(t, ident()) } + def idSelector(t: Tree): Tree = + atSpan(startOffset(t), in.offset) { Select(t, ident()) } + /** Selectors ::= id { `.' id } * * Accept `.' separated identifiers acting as a selectors on given tree `t`. @@ -1066,45 +1069,46 @@ object Parsers { if (in.token == DOT) { in.nextToken(); selectors(t, finish) } else t + def dotSelections(t: Tree): Tree = + if (in.token == DOT) { in.nextToken(); dotSelections(t) } + else t + private val id: Tree => Tree = x => x - /** Path ::= StableId - * | [id `.'] this - * - * @param thisOK If true, the path can end with the keyword `this`. - * If false, another selection is required after the `this`. - * @param finish An alternative parse in case the token following a `.' is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. + /** SimpleRef ::= id + * | [id ‘.’] ‘this’ + * | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id */ - def path(thisOK: Boolean, finish: Tree => Tree = id): Tree = { + def simpleRef(): Tree = val start = in.offset - def handleThis(qual: Ident) = { + + def handleThis(qual: Ident) = in.nextToken() - val t = atSpan(start) { This(qual) } - if (!thisOK && in.token != DOT) syntaxError(DanglingThisInPath(), t.span) - dotSelectors(t, finish) - } - def handleSuper(qual: Ident) = { + atSpan(start) { This(qual) } + + def handleSuper(qual: Ident) = in.nextToken() val mix = mixinQualifierOpt() val t = atSpan(start) { Super(This(qual), mix) } accept(DOT) - dotSelectors(selector(t), finish) - } - if (in.token == THIS) handleThis(EmptyTypeIdent) - else if (in.token == SUPER) handleSuper(EmptyTypeIdent) - else { + idSelector(t) + + if in.token == THIS then handleThis(EmptyTypeIdent) + else if in.token == SUPER then handleSuper(EmptyTypeIdent) + else val t = termIdent() - if (in.token == DOT) { + if in.token == DOT then def qual = cpy.Ident(t)(t.name.toTypeName) - in.nextToken() - if (in.token == THIS) handleThis(qual) - else if (in.token == SUPER) handleSuper(qual) - else selectors(t, finish) - } + in.lookahead.token match + case THIS => + in.nextToken() + handleThis(qual) + case SUPER => + in.nextToken() + handleSuper(qual) + case _ => t else t - } - } + end simpleRef /** MixinQualifier ::= `[' id `]' */ @@ -1112,13 +1116,6 @@ object Parsers { if (in.token == LBRACKET) inBrackets(atSpan(in.offset) { typeIdent() }) else EmptyTypeIdent - /** StableId ::= id - * | Path `.' id - * | [id '.'] super [`[' id `]']`.' id - */ - def stableId(): Tree = - path(thisOK = false) - /** QualId ::= id {`.' id} */ def qualId(): Tree = dotSelectors(termIdent()) @@ -1577,8 +1574,8 @@ object Parsers { /** SimpleType ::= SimpleType TypeArgs * | SimpleType `#' id - * | StableId - * | Path `.' type + * | Singleton `.' id + * | Singleton `.' type * | `(' ArgTypes `)' * | `_' TypeBounds * | Refinement @@ -1613,18 +1610,22 @@ object Parsers { } else if (isSplice) splice(isType = true) - else path(thisOK = false, handleSingletonType) match { - case r @ SingletonTypeTree(_) => r - case r => convertToTypeId(r) - } + else + singletonCompletion(simpleRef()) } - val handleSingletonType: Tree => Tree = t => - if (in.token == TYPE) { + /** Singleton ::= SimpleRef + * | Singleton ‘.’ id + */ + def singletonCompletion(t: Tree): Tree = + if in.token == DOT then in.nextToken() - atSpan(startOffset(t)) { SingletonTypeTree(t) } - } - else t + if in.token == TYPE then + in.nextToken() + atSpan(startOffset(t)) { SingletonTypeTree(t) } + else + singletonCompletion(idSelector(t)) + else convertToTypeId(t) private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) @@ -2207,7 +2208,7 @@ object Parsers { * | SimpleExpr1 [`_`] * SimpleExpr1 ::= literal * | xmlLiteral - * | Path + * | SimpleRef * | `(` [ExprsInParens] `)` * | SimpleExpr `.` id * | SimpleExpr `.` MatchClause @@ -2223,9 +2224,9 @@ object Parsers { xmlLiteral() case IDENTIFIER => if (isSplice) splice(isType = false) - else path(thisOK = true) + else simpleRef() case BACKQUOTED_IDENT | THIS | SUPER => - path(thisOK = true) + simpleRef() case USCORE => val start = in.skipToken() val pname = WildcardParamName.fresh() @@ -2658,17 +2659,16 @@ object Parsers { * | XmlPattern * | `(' [Patterns] `)' * | SimplePattern1 [TypeArgs] [ArgumentPatterns] - * SimplePattern1 ::= Path + * SimplePattern1 ::= SimpleRef * | SimplePattern1 `.' id * PatVar ::= id * | `_' */ val simplePattern: () => Tree = () => in.token match { - case IDENTIFIER | BACKQUOTED_IDENT | THIS => - path(thisOK = true) match { + case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER => + simpleRef() match case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(startOffset(id)) case t => simplePatternRest(t) - } case USCORE => val wildIdent = wildcardIdent() @@ -2694,14 +2694,17 @@ object Parsers { } } - def simplePatternRest(t: Tree): Tree = { - var p = t - if (in.token == LBRACKET) - p = atSpan(startOffset(t), in.offset) { TypeApply(p, typeArgs(namedOK = false, wildOK = false)) } - if (in.token == LPAREN) - p = atSpan(startOffset(t), in.offset) { Apply(p, argumentPatterns()) } - p - } + def simplePatternRest(t: Tree): Tree = + if in.token == DOT then + in.nextToken() + simplePatternRest(idSelector(t)) + else + var p = t + if (in.token == LBRACKET) + p = atSpan(startOffset(t), in.offset) { TypeApply(p, typeArgs(namedOK = false, wildOK = false)) } + if (in.token == LPAREN) + p = atSpan(startOffset(t), in.offset) { Apply(p, argumentPatterns()) } + p /** Patterns ::= Pattern [`,' Pattern] */ @@ -3088,7 +3091,7 @@ object Parsers { ctx.compilationUnit.sourceVersion = Some(SourceVersion.valueOf(imported.toString)) Import(tree, selectors) - /** ImportExpr ::= StableId ‘.’ ImportSpec + /** ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec * ImportSpec ::= id * | ‘_’ * | ‘{’ ImportSelectors) ‘}’ @@ -3135,26 +3138,24 @@ object Parsers { Nil selector :: rest - val handleImport: Tree => Tree = tree => + def importSelection(qual: Tree): Tree = + accept(DOT) in.token match case USCORE => - mkTree(tree, ImportSelector(wildcardSelectorId()) :: Nil) + mkTree(qual, ImportSelector(wildcardSelectorId()) :: Nil) case LBRACE => - mkTree(tree, inBraces(importSelectors(idOK = true))) + mkTree(qual, inBraces(importSelectors(idOK = true))) case _ => - tree - - () => { - val p = path(thisOK = false, handleImport) - p match - case _: Import | _: Export => p - case sel @ Select(qual, name) => - val selector = ImportSelector(atSpan(pointOffset(sel)) { Ident(name) }) - mkTree(qual, selector :: Nil).withSpan(sel.span) - case t => - accept(DOT) - mkTree(t, ImportSelector(Ident(nme.WILDCARD)) :: Nil) - } + val start = in.offset + val name = ident() + if in.token == DOT then + importSelection(atSpan(startOffset(qual), start) { Select(qual, name) }) + else + atSpan(startOffset(qual)) { + mkTree(qual, ImportSelector(atSpan(start) { Ident(name) }) :: Nil) + } + + () => importSelection(simpleRef()) } def posMods(start: Int, mods: Modifiers): Modifiers = { diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index d818d43e7132..55d99b0bf0da 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -900,7 +900,10 @@ object Scanners { // Lookahead --------------------------------------------------------------- /** The next token after this one. + * The token is computed via fetchToken, so complex two word + * tokens such as CASECLASS are not recognized. * Newlines and indent/unindent tokens are skipped. + * */ def lookahead: TokenData = if next.token == EMPTY then diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 373b625aca26..4e6bfbddf102 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -100,7 +100,7 @@ class TabcompleteTests extends ReplTest { @Test def importScala = fromInitialState { implicit s => val comp = tabComplete("import scala.") // check that there are no special symbols leaked: , , ... - assertEquals(comp.find(_.startsWith("<")), Some("<:<")) + assertEquals(Some("<:<"), comp.find(_.startsWith("<"))) assert(!comp.contains("package")) } From 5cbc8400913825207ca5cfd7e3e8d07e8950f128 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 1 May 2020 17:57:41 +0200 Subject: [PATCH 3/4] Refactor syntax for ConstrApps Split off the parts of SimpleType that - can be applied to type arguments, or selected with # - can be used in a constructor This gives a bit more precision for type and constructor parsing. It's also a necessary step to be able to add types that are applied to singletons. --- .../dotty/tools/dotc/parsing/Parsers.scala | 83 ++++++++++--------- docs/docs/internals/syntax.md | 37 +++++---- tests/neg/i4373.scala | 4 +- tests/neg/i4373c.scala | 2 +- 4 files changed, 67 insertions(+), 59 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4df5ebe9404a..db617fff8292 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1572,61 +1572,62 @@ object Parsers { if (isType) TypSplice(expr) else Splice(expr) } - /** SimpleType ::= SimpleType TypeArgs - * | SimpleType `#' id - * | Singleton `.' id - * | Singleton `.' type - * | `(' ArgTypes `)' - * | `_' TypeBounds - * | Refinement - * | Literal - * | ‘$’ ‘{’ Block ‘}’ + /** SimpleType ::= SimpleLiteral + * | ‘?’ SubtypeBounds + * | SimpleType1 */ - def simpleType(): Tree = simpleTypeRest { - if (in.token == LPAREN) - atSpan(in.offset) { - makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true))) - } - else if (in.token == LBRACE) - atSpan(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } - else if (isSimpleLiteral) { SingletonTypeTree(literal(inType = true)) } + def simpleType(): Tree = + if isSimpleLiteral then + SingletonTypeTree(literal(inType = true)) else if isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token) then val start = in.offset in.nextToken() SingletonTypeTree(literal(negOffset = start, inType = true)) - else if (in.token == USCORE) { + else if in.token == USCORE then if sourceVersion.isAtLeast(`3.1`) then deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead") patch(source, Span(in.offset, in.offset + 1), "?") val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) - } - else if (isIdent(nme.?)) { + else if isIdent(nme.?) then val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) - } - else if (isIdent(nme.*) && ctx.settings.YkindProjector.value) { + else if isIdent(nme.*) && ctx.settings.YkindProjector.value then typeIdent() - } + else + simpleType1() + + /** SimpleType1 ::= id + * | Singleton `.' id + * | Singleton `.' type + * | ‘(’ ArgTypes ‘)’ + * | Refinement + * | ‘$’ ‘{’ Block ‘}’ + * | SimpleType1 TypeArgs + * | SimpleType1 `#' id + */ + def simpleType1() = simpleTypeRest { + if in.token == LPAREN then + atSpan(in.offset) { + makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true))) + } + else if in.token == LBRACE then + atSpan(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } else if (isSplice) splice(isType = true) else + def singletonCompletion(t: Tree): Tree = + if in.token == DOT then + in.nextToken() + if in.token == TYPE then + in.nextToken() + atSpan(startOffset(t)) { SingletonTypeTree(t) } + else + singletonCompletion(idSelector(t)) + else convertToTypeId(t) singletonCompletion(simpleRef()) } - /** Singleton ::= SimpleRef - * | Singleton ‘.’ id - */ - def singletonCompletion(t: Tree): Tree = - if in.token == DOT then - in.nextToken() - if in.token == TYPE then - in.nextToken() - atSpan(startOffset(t)) { SingletonTypeTree(t) } - else - singletonCompletion(idSelector(t)) - else convertToTypeId(t) - private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) case LBRACKET => simpleTypeRest(atSpan(startOffset(t)) { @@ -3630,13 +3631,13 @@ object Parsers { /* -------- TEMPLATES ------------------------------------------- */ - /** SimpleConstrApp ::= AnnotType {ParArgumentExprs} + /** ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} */ - val constrApp: () => Tree = () => { - val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR)) - // Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code + val constrApp: () => Tree = () => + val t = rejectWildcardType(annotTypeRest(simpleType1()), + fallbackTree = Ident(tpnme.ERROR)) + // Using Ident(tpnme.ERROR) to avoid causing cascade errors on non-user-written code if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t - } /** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} */ diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 88c49035696c..46770818ad4d 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -128,11 +128,10 @@ Literal ::= SimpleLiteral QualId ::= id {‘.’ id} ids ::= id {‘,’ id} -Path ::= StableId +SimpleRef ::= id | [id ‘.’] ‘this’ -StableId ::= id - | Path ‘.’ id | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id + ClassQualifier ::= ‘[’ id ‘]’ ``` @@ -153,17 +152,25 @@ InfixType ::= RefinedType {id [nl] RefinedType} RefinedType ::= WithType {[nl | colonEol] Refinement} RefinedTypeTree(t, ds) WithType ::= AnnotType {‘with’ AnnotType} (deprecated) AnnotType ::= SimpleType {Annotation} Annotated(t, annot) -SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args) - | SimpleType ‘#’ id Select(t, name) - | StableId - | Path ‘.’ ‘type’ SingletonTypeTree(p) - | ‘(’ ArgTypes ‘)’ Tuple(ts) + +SimpleType ::= SimpleLiteral SingletonTypeTree(l) | ‘?’ SubtypeBounds + | SimpleType ‘(’ Singletons ‘)’ + | SimpleType1 +SimpleType1 ::= id Ident(name) + | Singleton ‘.’ id Select(t, name) + | Singleton ‘.’ ‘type’ SingletonTypeTree(p) + | ‘(’ ArgTypes ‘)’ Tuple(ts) | Refinement RefinedTypeTree(EmptyTree, refinement) - | SimpleLiteral SingletonTypeTree(l) | ‘$’ ‘{’ Block ‘}’ -ArgTypes ::= Type {‘,’ Type} - | NamedTypeArg {‘,’ NamedTypeArg} + | SimpleType1 TypeArgs AppliedTypeTree(t, args) + | SimpleType1 ‘#’ id Select(t, name) +Singleton ::= SimpleRef + | Singleton ‘.’ id +-- not yet | Singleton ‘(’ Singletons ‘)’ +-- not yet | Singleton ‘[’ ArgTypes ‘]’ +Singletons ::= Singleton { ‘,’ Singleton } +ArgTypes ::= Types FunArgType ::= Type | ‘=>’ Type PrefixOp(=>, t) ParamType ::= [‘=>’] ParamValueType @@ -209,7 +216,7 @@ InfixExpr ::= PrefixExpr | InfixExpr MatchClause MatchClause ::= ‘match’ ‘{’ CaseClauses ‘}’ Match(expr, cases) PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op) -SimpleExpr ::= Path +SimpleExpr ::= SimpleRef | Literal | ‘_’ | BlockExpr @@ -271,7 +278,7 @@ SimplePattern ::= PatVar | Quoted | XmlPattern | SimplePattern1 [TypeArgs] [ArgumentPatterns] -SimplePattern1 ::= Path +SimplePattern1 ::= SimpleRef | SimplePattern1 ‘.’ id PatVar ::= varid | ‘_’ @@ -335,7 +342,7 @@ AccessQualifier ::= ‘[’ id ‘]’ Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args) Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} -ImportExpr ::= StableId ‘.’ ImportSpec Import(expr, sels) +ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec Import(expr, sels) ImportSpec ::= id | ‘_’ | ‘{’ ImportSelectors) ‘}’ @@ -398,7 +405,7 @@ ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} -ConstrApp ::= AnnotType {ParArgumentExprs} Apply(tp, args) +ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} Apply(tp, args) ConstrExpr ::= SelfInvocation | ‘{’ SelfInvocation {semi BlockStat} ‘}’ SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} diff --git a/tests/neg/i4373.scala b/tests/neg/i4373.scala index 20a6c2595c9b..d3a7bce51436 100644 --- a/tests/neg/i4373.scala +++ b/tests/neg/i4373.scala @@ -9,13 +9,13 @@ class X5[A >: _ with X5[_]] // error class X6[A >: X6[_] with _] // error class A1 extends _ // error -class A2 extends _ with _ // error // error +class A2 extends _ with _ // error class A3 extends Base with _ // error class A4 extends _ with Base // error object Test { type T1 = _ // error - type T2 = _[Int] // error + type T2 = _[Int] // error // error type T3 = _ { type S } // error type T4 = [X] =>> _ // error diff --git a/tests/neg/i4373c.scala b/tests/neg/i4373c.scala index 44d744856f3f..c14d69da65d8 100644 --- a/tests/neg/i4373c.scala +++ b/tests/neg/i4373c.scala @@ -1,4 +1,4 @@ // ==> 18b253a4a89a84c5674165c6fc3efafad535eee3.scala <== object x0 { - def x1[x2 <:_[ // error // error + def x1[x2 <:_[ // error // error \ No newline at end of file From 6aa95a39f4dd01a6afa3df2b75d1c177e442aee4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 2 May 2020 14:01:10 +0200 Subject: [PATCH 4/4] Drop unused parsing methods --- .../dotty/tools/dotc/parsing/Parsers.scala | 38 +++++-------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index db617fff8292..62befc1e5a7a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1040,37 +1040,17 @@ object Parsers { atSpan(accept(USCORE)) { Ident(nme.WILDCARD) } /** Accept identifier or match clause acting as a selector on given tree `t` */ - def selector(t: Tree): Tree = + def selectorOrMatch(t: Tree): Tree = atSpan(startOffset(t), in.offset) { if in.token == MATCH then matchClause(t) else Select(t, ident()) } - def idSelector(t: Tree): Tree = + def selector(t: Tree): Tree = atSpan(startOffset(t), in.offset) { Select(t, ident()) } - /** Selectors ::= id { `.' id } - * - * Accept `.' separated identifiers acting as a selectors on given tree `t`. - * @param finish An alternative parse in case the next token is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def selectors(t: Tree, finish: Tree => Tree): Tree = { - val t1 = finish(t) - if (t1 ne t) t1 else dotSelectors(selector(t), finish) - } - - /** DotSelectors ::= { `.' id } - * - * Accept `.' separated identifiers acting as a selectors on given tree `t`. - * @param finish An alternative parse in case the token following a `.' is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def dotSelectors(t: Tree, finish: Tree => Tree = id): Tree = - if (in.token == DOT) { in.nextToken(); selectors(t, finish) } - else t - - def dotSelections(t: Tree): Tree = - if (in.token == DOT) { in.nextToken(); dotSelections(t) } + /** DotSelectors ::= { `.' id } */ + def dotSelectors(t: Tree): Tree = + if (in.token == DOT) { in.nextToken(); dotSelectors(selector(t)) } else t private val id: Tree => Tree = x => x @@ -1091,7 +1071,7 @@ object Parsers { val mix = mixinQualifierOpt() val t = atSpan(start) { Super(This(qual), mix) } accept(DOT) - idSelector(t) + selector(t) if in.token == THIS then handleThis(EmptyTypeIdent) else if in.token == SUPER then handleSuper(EmptyTypeIdent) @@ -1623,7 +1603,7 @@ object Parsers { in.nextToken() atSpan(startOffset(t)) { SingletonTypeTree(t) } else - singletonCompletion(idSelector(t)) + singletonCompletion(selector(t)) else convertToTypeId(t) singletonCompletion(simpleRef()) } @@ -2282,7 +2262,7 @@ object Parsers { in.token match { case DOT => in.nextToken() - simpleExprRest(selector(t), canApply = true) + simpleExprRest(selectorOrMatch(t), canApply = true) case LBRACKET => val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) } simpleExprRest(tapp, canApply = true) @@ -2698,7 +2678,7 @@ object Parsers { def simplePatternRest(t: Tree): Tree = if in.token == DOT then in.nextToken() - simplePatternRest(idSelector(t)) + simplePatternRest(selector(t)) else var p = t if (in.token == LBRACKET)