Skip to content

Commit 96afde6

Browse files
committed
[parser] parse constructor annotations correctly in scala 3 (#SCL-21217, #SCL-21216, #SCL-18495) fixed
needed to increase getStubVersion because ScAnnotationElementType.annotationText has changed
1 parent 2f165e1 commit 96afde6

File tree

11 files changed

+1023
-53
lines changed

11 files changed

+1023
-53
lines changed

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/base/Constructor.scala

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ import org.jetbrains.plugins.scala.lang.parser.ScalaElementType
55
import org.jetbrains.plugins.scala.lang.parser.parsing.ParsingRule
66
import org.jetbrains.plugins.scala.lang.parser.parsing.builder.ScalaPsiBuilder
77
import org.jetbrains.plugins.scala.lang.parser.parsing.expressions.ArgumentExprs
8+
import org.jetbrains.plugins.scala.lang.parser.parsing.top.params.ClassParam
89
import org.jetbrains.plugins.scala.lang.parser.parsing.types.{AnnotType, SimpleType}
910

1011
/*
1112
* Constr ::= AnnotType {ArgumentExprs}
1213
*/
1314
object Constructor extends ParsingRule {
1415

15-
override def parse(implicit builder: ScalaPsiBuilder): Boolean = parse(isAnnotation = false)
16+
override def parse(implicit builder: ScalaPsiBuilder): Boolean = parse(isAnnotation = false, stopBeforeNonEmptyParameterClause = false)
1617

17-
def parse(isAnnotation: Boolean)(implicit builder: ScalaPsiBuilder): Boolean = {
18+
def parse(isAnnotation: Boolean, stopBeforeNonEmptyParameterClause: Boolean)(implicit builder: ScalaPsiBuilder): Boolean = {
1819
val constrMarker = builder.mark()
1920
val latestDoneMarker = builder.getLatestDoneMarker
2021

@@ -23,25 +24,51 @@ object Constructor extends ParsingRule {
2324
latestDoneMarker.getTokenType != ScalaElementType.MODIFIERS &&
2425
latestDoneMarker.getTokenType != ScalaElementType.TYPE_PARAM_CLAUSE)
2526

26-
if ((!isAnnotation && !AnnotType(isPattern = false, multipleSQBrackets = false)) ||
27-
(isAnnotation && !SimpleType(isPattern = false))) {
27+
val parsedType =
28+
if (isAnnotation) {
29+
SimpleType(isPattern = false)
30+
} else {
31+
AnnotType(isPattern = false, multipleSQBrackets = false)
32+
}
33+
34+
if (!parsedType) {
2835
constrMarker.drop()
2936
return false
3037
}
3138

32-
if (builder.getTokenType == ScalaTokenTypes.tLPARENTHESIS) {
33-
if (ArgumentExprs())
39+
if (builder.isScala3) {
40+
var first = true
3441

3542
while (
3643
builder.getTokenType == ScalaTokenTypes.tLPARENTHESIS &&
37-
(!isAnnotation || annotationAllowed) &&
44+
(!stopBeforeNonEmptyParameterClause || !isParameter(first)) &&
3845
ArgumentExprs()
3946
) {
40-
// already parsed ArgumentExprs
47+
first = false
48+
}
49+
50+
} else {
51+
if (builder.getTokenType == ScalaTokenTypes.tLPARENTHESIS) {
52+
if (ArgumentExprs()) {
53+
while (
54+
builder.getTokenType == ScalaTokenTypes.tLPARENTHESIS &&
55+
(!isAnnotation || annotationAllowed) &&
56+
ArgumentExprs()
57+
) {
58+
// already parsed ArgumentExprs
59+
}
60+
}
4161
}
4262
}
4363

4464
constrMarker.done(ScalaElementType.CONSTRUCTOR)
4565
true
4666
}
67+
68+
private def isParameter(first: Boolean)(implicit builder: ScalaPsiBuilder): Boolean = builder.predict { builder =>
69+
builder.getTokenType == ScalaTokenTypes.kIMPLICIT ||
70+
builder.getTokenText == "using" ||
71+
(!first && builder.getTokenType == ScalaTokenTypes.tRPARENTHESIS) ||
72+
ClassParam.parse(ignoreErrors = false)(builder)
73+
}
4774
}

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/expressions/Annotation.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.annotation.tailrec
1717
*/
1818
object Annotation {
1919

20-
def apply(countLinesAfterAnnotation: Boolean = true)(implicit builder: ScalaPsiBuilder): Boolean = {
20+
def apply(countLinesAfterAnnotation: Boolean = true, forConstructor: Boolean = false)(implicit builder: ScalaPsiBuilder): Boolean = {
2121
if (builder.getTokenType != ScalaTokenTypes.tAT)
2222
return false
2323

@@ -26,13 +26,13 @@ object Annotation {
2626

2727
builder.advanceLexer() //Ate @
2828

29-
if (!AnnotationExpr()) {
29+
if (!AnnotationExpr(forConstructor)) {
3030
builder error ScalaBundle.message("wrong.annotation.expression")
3131
annotMarker.drop()
3232
} else {
3333
annotMarker.done(ScalaElementType.ANNOTATION)
3434
}
35-
if (countLinesAfterAnnotation && builder.twoNewlinesBeforeCurrentToken) {
35+
if (!builder.isScala3 && countLinesAfterAnnotation && builder.twoNewlinesBeforeCurrentToken) {
3636
rollbackMarker.rollbackTo()
3737
return false
3838
} else rollbackMarker.drop()

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/expressions/AnnotationExpr.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import org.jetbrains.plugins.scala.lang.parser.parsing.builder.ScalaPsiBuilder
88
/*
99
* AnnotationExpr ::= Constr [[nl] '{' {NameValuePair} '}']
1010
*/
11-
object AnnotationExpr extends ParsingRule {
12-
13-
override def parse(implicit builder: ScalaPsiBuilder): Boolean = {
11+
object AnnotationExpr {
12+
def apply(forConstructor: Boolean)(implicit builder: ScalaPsiBuilder): Boolean = {
1413
val annotExprMarker = builder.mark()
15-
if (Constructor.parse(isAnnotation = true)) {
14+
if (Constructor.parse(isAnnotation = true, stopBeforeNonEmptyParameterClause = forConstructor)) {
1615
annotExprMarker.done(ScalaElementType.ANNOTATION_EXPR)
1716
true
1817
} else {

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/expressions/Annotations.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ private[parsing] object Annotations extends ParsingRule {
2121
true
2222
}
2323

24-
def parseOnTheSameLine()(implicit builder: ScalaPsiBuilder): Boolean = {
25-
parseAnnotations(builder.newlineBeforeCurrentToken)
24+
def parseForConstructor()(implicit builder: ScalaPsiBuilder): Boolean = {
25+
parseAnnotations(builder.newlineBeforeCurrentToken, forConstructor = true)
2626
true
2727
}
2828

@@ -37,12 +37,12 @@ private[parsing] object Annotations extends ParsingRule {
3737
marker.setCustomEdgeTokenBinders(LeftEdgeBinder, null)
3838
}
3939

40-
private def parseAnnotations(newlineBeforeCurrentToken: Boolean = false)
40+
private def parseAnnotations(newlineBeforeCurrentToken: Boolean = false, forConstructor: Boolean = false)
4141
(implicit builder: ScalaPsiBuilder) = {
4242
val marker = builder.mark()
4343

4444
if (!newlineBeforeCurrentToken) {
45-
while (Annotation()) {}
45+
while (Annotation(forConstructor = forConstructor)) {}
4646
}
4747
marker.done(ScalaElementType.ANNOTATIONS)
4848

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/top/ConstrMods.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import org.jetbrains.plugins.scala.lang.parser.parsing.expressions.Annotations
1212
object ConstrMods extends ParsingRule {
1313

1414
override def parse(implicit builder: ScalaPsiBuilder): Boolean = {
15-
Annotations.parseOnTheSameLine()
15+
Annotations.parseForConstructor()
1616

1717
val modifiersMarker = builder.mark()
1818
if (!builder.newlineBeforeCurrentToken) {

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/top/params/ClassParam.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ import org.jetbrains.plugins.scala.lang.parser.parsing.types.ParamType
1515
*/
1616
object ClassParam extends ParsingRule {
1717

18-
override def parse(implicit builder: ScalaPsiBuilder): Boolean = {
18+
override def parse(implicit builder: ScalaPsiBuilder): Boolean = parse(ignoreErrors = true)
19+
20+
def parse(ignoreErrors: Boolean)(implicit builder: ScalaPsiBuilder): Boolean = {
1921
val classParamMarker = builder.mark()
2022

2123
Annotations()
24+
var hasError = false
2225

2326
//parse modifiers
2427
val modifierMarker = builder.mark()
@@ -55,9 +58,11 @@ object ClassParam extends ParsingRule {
5558
builder.advanceLexer() //Ate ':'
5659
if (!ParamType()) {
5760
builder.error(ScalaBundle.message("parameter.type.expected"))
61+
hasError = true
5862
}
5963
case _ =>
6064
builder.error(ScalaBundle.message("colon.expected"))
65+
hasError = true
6166
}
6267

6368
//default param
@@ -66,11 +71,12 @@ object ClassParam extends ParsingRule {
6671
builder.advanceLexer() //Ate '='
6772
if (!Expr()) {
6873
builder.wrongExpressionError()
74+
hasError = true
6975
}
7076
case _ =>
7177
}
7278
classParamMarker.done(ScalaElementType.CLASS_PARAM)
73-
true
79+
ignoreErrors || !hasError
7480
}
7581

7682
private val canFollowInlineKeyword = Set(

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/compiled/ScClassFileDecompiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ object ScClassFileDecompiler {
3939

4040
object ScClsStubBuilder extends compiled.ClsStubBuilder {
4141

42-
override val getStubVersion = 438
42+
override val getStubVersion = 439
4343

4444
override def buildFileStub(content: FileContent): stubs.PsiFileStubImpl[_ <: PsiFile] =
4545
decompiledScalaFile(content)

0 commit comments

Comments
 (0)