mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java Parser & stubs: recognize local record if it has type parameter, do not remap tokens until sure
GitOrigin-RevId: aab508cd6f840047ab950c0b428c1a1f18e0aa1c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2ea07c424a
commit
573faaae2c
@@ -64,15 +64,25 @@ public class DeclarationParser {
|
||||
@Nullable
|
||||
private PsiBuilder.Marker parseClassFromKeyword(PsiBuilder builder, PsiBuilder.Marker declaration, boolean isAnnotation, Context context) {
|
||||
IElementType keywordTokenType = builder.getTokenType();
|
||||
boolean isRecord = isRecordToken(builder, keywordTokenType);
|
||||
final boolean isRecord = isRecordToken(builder, keywordTokenType);
|
||||
if (isRecord) {
|
||||
if (builder.lookAhead(1) == JavaTokenType.IDENTIFIER) {
|
||||
builder.remapCurrentToken(JavaTokenType.RECORD_KEYWORD);
|
||||
keywordTokenType = JavaTokenType.RECORD_KEYWORD;
|
||||
} else {
|
||||
if (builder.lookAhead(1) != JavaTokenType.IDENTIFIER) {
|
||||
declaration.drop();
|
||||
return null;
|
||||
}
|
||||
final IElementType afterIdent = builder.lookAhead(2);
|
||||
// No parser recovery for local records without < or ( to support for light stubs
|
||||
// (look at com.intellij.psi.impl.source.JavaLightStubBuilder.CodeBlockVisitor.visit)
|
||||
if (context == Context.CODE_BLOCK && afterIdent != JavaTokenType.LPARENTH && afterIdent != JavaTokenType.LT) {
|
||||
// skipping record kw and identifier
|
||||
builder.advanceLexer();
|
||||
builder.advanceLexer();
|
||||
error(builder, JavaErrorBundle.message("expected.lt.or.lparen"));
|
||||
declaration.drop();
|
||||
return null;
|
||||
}
|
||||
builder.remapCurrentToken(JavaTokenType.RECORD_KEYWORD);
|
||||
keywordTokenType = JavaTokenType.RECORD_KEYWORD;
|
||||
}
|
||||
assert ElementType.CLASS_KEYWORD_BIT_SET.contains(keywordTokenType) : keywordTokenType;
|
||||
builder.advanceLexer();
|
||||
@@ -90,11 +100,6 @@ public class DeclarationParser {
|
||||
if (builder.getTokenType() == JavaTokenType.LPARENTH) {
|
||||
parseElementList(builder, ListType.RECORD_COMPONENTS);
|
||||
}
|
||||
else if (isRecord && context == Context.CODE_BLOCK) {
|
||||
error(builder, JavaErrorBundle.message("expected.lparen"));
|
||||
declaration.drop();
|
||||
return null;
|
||||
}
|
||||
|
||||
refParser.parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_LIST, JavaTokenType.COMMA);
|
||||
refParser.parseReferenceList(builder, JavaTokenType.IMPLEMENTS_KEYWORD, JavaElementType.IMPLEMENTS_LIST, JavaTokenType.COMMA);
|
||||
|
||||
@@ -157,8 +157,12 @@ public class JavaLightStubBuilder extends LightStubBuilder {
|
||||
return (result = false);
|
||||
}
|
||||
// if record is inside lazy parseable element, tokens are not remapped and record token is still identifier
|
||||
// This token combination may be "record RecordName ("
|
||||
else if (preLast == JavaTokenType.IDENTIFIER && last == JavaTokenType.IDENTIFIER && type == JavaTokenType.LPARENTH) {
|
||||
// This token combination may be "record RecordName (" or "record RecordName<..."
|
||||
// Local records without < or ( won't be parsed
|
||||
else if (preLast == JavaTokenType.IDENTIFIER &&
|
||||
last == JavaTokenType.IDENTIFIER &&
|
||||
(type == JavaTokenType.LPARENTH || type == JavaTokenType.LT)
|
||||
) {
|
||||
return (result = false);
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,7 @@ expected.module.declaration=Module declaration expected
|
||||
expected.module.statement=Module directive expected
|
||||
expected.switch.label='case', 'default' or '}' expected
|
||||
expected.switch.rule=Expression, block or throw statement expected
|
||||
expected.lt.or.lparen='<' or '(' expected
|
||||
|
||||
error.cannot.resolve.class=Cannot resolve class ''{0}''
|
||||
error.cannot.resolve.class.or.package=Cannot resolve class or package ''{0}''
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
void foo() {
|
||||
record R<T>{};
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
PsiJavaFile:LocalRecordWithTypeParams.java
|
||||
PsiImportList
|
||||
<empty list>
|
||||
PsiClass:A
|
||||
PsiModifierList:
|
||||
<empty list>
|
||||
PsiKeyword:class('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiIdentifier:A('A')
|
||||
PsiTypeParameterList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiJavaToken:LBRACE('{')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiMethod:foo
|
||||
PsiModifierList:
|
||||
<empty list>
|
||||
PsiTypeParameterList
|
||||
<empty list>
|
||||
PsiTypeElement:void
|
||||
PsiKeyword:void('void')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiIdentifier:foo('foo')
|
||||
PsiParameterList:()
|
||||
PsiJavaToken:LPARENTH('(')
|
||||
PsiJavaToken:RPARENTH(')')
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiCodeBlock
|
||||
PsiJavaToken:LBRACE('{')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiDeclarationStatement
|
||||
PsiClass:R
|
||||
PsiModifierList:
|
||||
<empty list>
|
||||
PsiKeyword:record('record')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiIdentifier:R('R')
|
||||
PsiTypeParameterList
|
||||
PsiJavaToken:LT('<')
|
||||
PsiTypeParameter:T
|
||||
PsiIdentifier:T('T')
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiJavaToken:GT('>')
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiJavaToken:LBRACE('{')
|
||||
PsiJavaToken:RBRACE('}')
|
||||
PsiEmptyStatement
|
||||
PsiJavaToken:SEMICOLON(';')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiJavaToken:RBRACE('}')
|
||||
PsiWhiteSpace('\n')
|
||||
PsiJavaToken:RBRACE('}')
|
||||
+2
-4
@@ -37,12 +37,10 @@ PsiJavaFile:LocalRecordWithoutParens.java
|
||||
PsiDeclarationStatement
|
||||
PsiModifierList:
|
||||
<empty list>
|
||||
PsiKeyword:record('record')
|
||||
PsiIdentifier:record('record')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiIdentifier:R('R')
|
||||
PsiTypeParameterList
|
||||
<empty list>
|
||||
PsiErrorElement:'(' expected
|
||||
PsiErrorElement:'<' or '(' expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiBlockStatement
|
||||
|
||||
+1
@@ -53,6 +53,7 @@ public class ClassParsingTest extends JavaParsingTestCase {
|
||||
public void testRecordWithModifiers() { doTest(true); }
|
||||
public void testRecordInCodeBlock() { doTest(true); }
|
||||
public void testLocalRecord() { doTest(true); }
|
||||
public void testLocalRecordWithTypeParams() { doTest(true); }
|
||||
public void testLocalRecordWithoutParens() { doTest(true); }
|
||||
public void testCompactConstructor0() { doTest(true); }
|
||||
public void testCompactConstructor1() { doTest(true); }
|
||||
|
||||
@@ -590,8 +590,89 @@ public class JavaStubBuilderTest extends LightIdeaTestCase {
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" PARAMETER_LIST:PsiParameterListStub\n" +
|
||||
" THROWS_LIST:PsiRefListStub[THROWS_LIST:]\n");
|
||||
}
|
||||
|
||||
public void testLocalRecordLikeIncompleteCode() {
|
||||
doTest("class A {\n" +
|
||||
" void foo(){\n" +
|
||||
" record turn getTitle();\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
|
||||
"PsiJavaFileStub []\n" +
|
||||
" IMPORT_LIST:PsiImportListStub\n" +
|
||||
" CLASS:PsiClassStub[name=A fqn=A]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" EXTENDS_LIST:PsiRefListStub[EXTENDS_LIST:]\n" +
|
||||
" IMPLEMENTS_LIST:PsiRefListStub[IMPLEMENTS_LIST:]\n" +
|
||||
" METHOD:PsiMethodStub[foo:void]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" PARAMETER_LIST:PsiParameterListStub\n" +
|
||||
" THROWS_LIST:PsiRefListStub[THROWS_LIST:]\n");
|
||||
}
|
||||
|
||||
|
||||
public void testLocalRecordLikeIncompleteCodeWithTypeParameters() {
|
||||
doTest("class A {\n" +
|
||||
" void foo(){\n" +
|
||||
" record turn<A>" +
|
||||
" }\n" +
|
||||
"}",
|
||||
|
||||
"PsiJavaFileStub []\n" +
|
||||
" IMPORT_LIST:PsiImportListStub\n" +
|
||||
" CLASS:PsiClassStub[name=A fqn=A]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" EXTENDS_LIST:PsiRefListStub[EXTENDS_LIST:]\n" +
|
||||
" IMPLEMENTS_LIST:PsiRefListStub[IMPLEMENTS_LIST:]\n" +
|
||||
" METHOD:PsiMethodStub[foo:void]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" PARAMETER_LIST:PsiParameterListStub\n" +
|
||||
" THROWS_LIST:PsiRefListStub[THROW" +
|
||||
"S_LIST:]\n");
|
||||
"S_LIST:]\n" +
|
||||
" CLASS:PsiClassStub[record name=turn fqn=null]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" TYPE_PARAMETER:PsiTypeParameter[A]\n" +
|
||||
" EXTENDS_BOUND_LIST:PsiRefListStub[EXTENDS_BOUNDS_LIST:]\n" +
|
||||
" EXTENDS_LIST:PsiRefListStub[EXTENDS_LIST:]\n" +
|
||||
" IMPLEMENTS_LIST:PsiRefListStub[IMPLEMENTS_LIST:]\n");
|
||||
}
|
||||
|
||||
public void testLocalRecordWithTypeParameters() {
|
||||
doTest("class A {\n" +
|
||||
" void foo(){\n" +
|
||||
" record R<String>(){}\n" +
|
||||
" " +
|
||||
"}\n" +
|
||||
"}",
|
||||
|
||||
"PsiJavaFileStub []\n" +
|
||||
" IMPORT_LIST:PsiImportListStub\n" +
|
||||
" CLASS:PsiClassStub[name=A fqn=A]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" EXTENDS_LIST:PsiRefListStub[EXTENDS_LIST:]\n" +
|
||||
" IMPLEMENTS_LIST:PsiRefListStub[IMPLEMENTS_LIST:]\n" +
|
||||
" METHOD:PsiMethodStub[foo:void]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" PARAMETER_LIST:PsiParameterListStub\n" +
|
||||
" THROWS_LIST:PsiRefListStub[THROW" +
|
||||
"S_LIST:]\n" +
|
||||
" CLASS:PsiClassStub[record name=R fqn=null]\n" +
|
||||
" MODIFIER_LIST:PsiModifierListStub[mask=0]\n" +
|
||||
" TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" +
|
||||
" TYPE_PARAMETER:PsiTypeParameter[String]\n" +
|
||||
" EXTENDS_BOUND_LIST:PsiRefListStub[EXTENDS_BOUNDS_LIST:]\n" +
|
||||
" RECORD_HEADER:PsiRecordHeaderStub\n" +
|
||||
" EXTENDS_LIST:PsiRefListStub[EXTENDS_LIST:]\n" +
|
||||
" IMPLEMENTS_LIST:PsiRefListStub[IMPLEMENTS_LIST:]\n");
|
||||
}
|
||||
|
||||
public void testSOEProof() {
|
||||
|
||||
Reference in New Issue
Block a user