Parsing dynamic in extension receiver position
This commit is contained in:
@@ -1527,7 +1527,8 @@ public class JetParsing extends AbstractJetParsing {
|
|||||||
parseAnnotations(REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS);
|
parseAnnotations(REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS);
|
||||||
|
|
||||||
IElementType lookahead = lookahead(1);
|
IElementType lookahead = lookahead(1);
|
||||||
if (at(IDENTIFIER) && lookahead(1) != DOT && lookahead != LT && lookahead != LPAR && at(DYNAMIC_KEYWORD)) {
|
IElementType lookahead2 = lookahead(2);
|
||||||
|
if (at(IDENTIFIER) && !(lookahead == DOT && lookahead2 == IDENTIFIER) && lookahead != LT && at(DYNAMIC_KEYWORD)) {
|
||||||
PsiBuilder.Marker dynamicType = mark();
|
PsiBuilder.Marker dynamicType = mark();
|
||||||
advance(); // DYNAMIC_KEYWORD
|
advance(); // DYNAMIC_KEYWORD
|
||||||
dynamicType.done(DYNAMIC_TYPE);
|
dynamicType.done(DYNAMIC_TYPE);
|
||||||
|
|||||||
+4
@@ -17,6 +17,8 @@
|
|||||||
package org.jetbrains.jet.lang.parsing;
|
package org.jetbrains.jet.lang.parsing;
|
||||||
|
|
||||||
import com.intellij.lang.PsiBuilder;
|
import com.intellij.lang.PsiBuilder;
|
||||||
|
import com.intellij.psi.tree.IElementType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public interface SemanticWhitespaceAwarePsiBuilder extends PsiBuilder {
|
public interface SemanticWhitespaceAwarePsiBuilder extends PsiBuilder {
|
||||||
// TODO: comments go to wrong place when an empty element is created, see IElementType.isLeftBound()
|
// TODO: comments go to wrong place when an empty element is created, see IElementType.isLeftBound()
|
||||||
@@ -29,4 +31,6 @@ public interface SemanticWhitespaceAwarePsiBuilder extends PsiBuilder {
|
|||||||
void restoreJoiningComplexTokensState();
|
void restoreJoiningComplexTokensState();
|
||||||
void enableJoiningComplexTokens();
|
void enableJoiningComplexTokens();
|
||||||
void disableJoiningComplexTokens();
|
void disableJoiningComplexTokens();
|
||||||
|
|
||||||
|
boolean isWhitespaceOrComment(@NotNull IElementType elementType);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -17,6 +17,8 @@
|
|||||||
package org.jetbrains.jet.lang.parsing;
|
package org.jetbrains.jet.lang.parsing;
|
||||||
|
|
||||||
import com.intellij.lang.impl.PsiBuilderAdapter;
|
import com.intellij.lang.impl.PsiBuilderAdapter;
|
||||||
|
import com.intellij.psi.tree.IElementType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class SemanticWhitespaceAwarePsiBuilderAdapter extends PsiBuilderAdapter implements SemanticWhitespaceAwarePsiBuilder {
|
public class SemanticWhitespaceAwarePsiBuilderAdapter extends PsiBuilderAdapter implements SemanticWhitespaceAwarePsiBuilder {
|
||||||
|
|
||||||
@@ -62,4 +64,9 @@ public class SemanticWhitespaceAwarePsiBuilderAdapter extends PsiBuilderAdapter
|
|||||||
public void disableJoiningComplexTokens() {
|
public void disableJoiningComplexTokens() {
|
||||||
myBuilder.disableJoiningComplexTokens();
|
myBuilder.disableJoiningComplexTokens();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWhitespaceOrComment(@NotNull IElementType elementType) {
|
||||||
|
return myBuilder.isWhitespaceOrComment(elementType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -19,9 +19,12 @@ package org.jetbrains.jet.lang.parsing;
|
|||||||
|
|
||||||
import com.intellij.lang.PsiBuilder;
|
import com.intellij.lang.PsiBuilder;
|
||||||
import com.intellij.lang.impl.PsiBuilderAdapter;
|
import com.intellij.lang.impl.PsiBuilderAdapter;
|
||||||
|
import com.intellij.lang.impl.PsiBuilderImpl;
|
||||||
import com.intellij.psi.TokenType;
|
import com.intellij.psi.TokenType;
|
||||||
import com.intellij.psi.tree.IElementType;
|
import com.intellij.psi.tree.IElementType;
|
||||||
import com.intellij.psi.tree.TokenSet;
|
import com.intellij.psi.tree.TokenSet;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
|
|
||||||
import com.intellij.util.containers.Stack;
|
import com.intellij.util.containers.Stack;
|
||||||
@@ -35,10 +38,36 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
|
|||||||
|
|
||||||
private final Stack<Boolean> newlinesEnabled = new Stack<Boolean>();
|
private final Stack<Boolean> newlinesEnabled = new Stack<Boolean>();
|
||||||
|
|
||||||
|
private final PsiBuilderImpl delegateImpl;
|
||||||
|
|
||||||
public SemanticWhitespaceAwarePsiBuilderImpl(PsiBuilder delegate) {
|
public SemanticWhitespaceAwarePsiBuilderImpl(PsiBuilder delegate) {
|
||||||
super(delegate);
|
super(delegate);
|
||||||
newlinesEnabled.push(true);
|
newlinesEnabled.push(true);
|
||||||
joinComplexTokens.push(true);
|
joinComplexTokens.push(true);
|
||||||
|
|
||||||
|
delegateImpl = findPsiBuilderImpl(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static PsiBuilderImpl findPsiBuilderImpl(PsiBuilder builder) {
|
||||||
|
// This is a hackish workaround for PsiBuilder interface not exposing isWhitespaceOrComment() method
|
||||||
|
// We have to unwrap all the adapters to find an Impl inside
|
||||||
|
while (true) {
|
||||||
|
if (builder instanceof PsiBuilderImpl) {
|
||||||
|
return (PsiBuilderImpl) builder;
|
||||||
|
}
|
||||||
|
if (!(builder instanceof PsiBuilderAdapter)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = ((PsiBuilderAdapter) builder).getDelegate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWhitespaceOrComment(@NotNull IElementType elementType) {
|
||||||
|
assert delegateImpl != null : "PsiBuilderImpl not found";
|
||||||
|
return delegateImpl.whitespaceOrComment(elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+32
-2
@@ -30,8 +30,7 @@ public class TruncatedSemanticWhitespaceAwarePsiBuilder extends SemanticWhitespa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eof() {
|
public boolean eof() {
|
||||||
if (super.eof()) return true;
|
return super.eof() || isOffsetBeyondEof(getCurrentOffset());
|
||||||
return myEOFPosition >= 0 && getCurrentOffset() >= myEOFPosition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,4 +45,35 @@ public class TruncatedSemanticWhitespaceAwarePsiBuilder extends SemanticWhitespa
|
|||||||
return super.getTokenType();
|
return super.getTokenType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IElementType lookAhead(int steps) {
|
||||||
|
if (eof()) return null;
|
||||||
|
|
||||||
|
int rawLookAheadSteps = rawLookAhead(steps);
|
||||||
|
if (isOffsetBeyondEof(rawTokenTypeStart(rawLookAheadSteps))) return null;
|
||||||
|
|
||||||
|
return super.rawLookup(rawLookAheadSteps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int rawLookAhead(int steps) {
|
||||||
|
// This code reproduces the behavior of PsiBuilderImpl.lookAhead(), but returns a number of raw steps instead of a token type
|
||||||
|
// This is required for implementing truncated builder behavior
|
||||||
|
int cur = 0;
|
||||||
|
while (steps > 0) {
|
||||||
|
cur++;
|
||||||
|
|
||||||
|
IElementType rawTokenType = rawLookup(cur);
|
||||||
|
while (rawTokenType != null && isWhitespaceOrComment(rawTokenType)) {
|
||||||
|
cur++;
|
||||||
|
rawTokenType = rawLookup(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
steps--;
|
||||||
|
}
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOffsetBeyondEof(int offsetFromCurrent) {
|
||||||
|
return myEOFPosition >= 0 && offsetFromCurrent >= myEOFPosition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// MODULE[js]: m1
|
// MODULE[js]: m1
|
||||||
// FILE: k.kt
|
// FILE: k.kt
|
||||||
|
|
||||||
fun (dynamic).test() {
|
fun dynamic.test() {
|
||||||
val v1 = foo()
|
val v1 = foo()
|
||||||
v1.isDynamic() // to check that anything is resolvable
|
v1.isDynamic() // to check that anything is resolvable
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
fun dynamic.foo()
|
||||||
|
fun dynamic?.foo()
|
||||||
|
val dynamic.foo: Int
|
||||||
|
val dynamic?.foo: Int
|
||||||
|
|
||||||
|
val foo: dynamic.() -> Unit
|
||||||
|
|
||||||
|
// testing look-ahead with comments and whitespace
|
||||||
|
|
||||||
|
fun dynamic . foo()
|
||||||
|
fun dynamic
|
||||||
|
.foo()
|
||||||
|
fun dynamic// line-comment
|
||||||
|
.foo()
|
||||||
|
fun dynamic/*
|
||||||
|
*/.foo()
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
JetFile: DynamicReceiver.kt
|
||||||
|
PACKAGE_DIRECTIVE
|
||||||
|
<empty list>
|
||||||
|
FUN
|
||||||
|
PsiElement(fun)('fun')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n')
|
||||||
|
FUN
|
||||||
|
PsiElement(fun)('fun')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
NULLABLE_TYPE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiElement(QUEST)('?')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n')
|
||||||
|
PROPERTY
|
||||||
|
PsiElement(val)('val')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
PsiElement(COLON)(':')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
USER_TYPE
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('Int')
|
||||||
|
PsiWhiteSpace('\n')
|
||||||
|
PROPERTY
|
||||||
|
PsiElement(val)('val')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
NULLABLE_TYPE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiElement(QUEST)('?')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
PsiElement(COLON)(':')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
USER_TYPE
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('Int')
|
||||||
|
PsiWhiteSpace('\n\n')
|
||||||
|
PROPERTY
|
||||||
|
PsiElement(val)('val')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
PsiElement(COLON)(':')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
FUNCTION_TYPE
|
||||||
|
FUNCTION_TYPE_RECEIVER
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
PsiElement(ARROW)('->')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
USER_TYPE
|
||||||
|
REFERENCE_EXPRESSION
|
||||||
|
PsiElement(IDENTIFIER)('Unit')
|
||||||
|
PsiWhiteSpace('\n\n')
|
||||||
|
PsiComment(EOL_COMMENT)('// testing look-ahead with comments and whitespace')
|
||||||
|
PsiWhiteSpace('\n\n')
|
||||||
|
FUN
|
||||||
|
PsiElement(fun)('fun')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n')
|
||||||
|
FUN
|
||||||
|
PsiElement(fun)('fun')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiWhiteSpace('\n ')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n')
|
||||||
|
FUN
|
||||||
|
PsiElement(fun)('fun')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiComment(EOL_COMMENT)('// line-comment')
|
||||||
|
PsiWhiteSpace('\n ')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
|
PsiWhiteSpace('\n')
|
||||||
|
FUN
|
||||||
|
PsiElement(fun)('fun')
|
||||||
|
PsiWhiteSpace(' ')
|
||||||
|
TYPE_REFERENCE
|
||||||
|
DYNAMIC_TYPE
|
||||||
|
PsiElement(dynamic)('dynamic')
|
||||||
|
PsiComment(BLOCK_COMMENT)('/*\n*/')
|
||||||
|
PsiElement(DOT)('.')
|
||||||
|
PsiElement(IDENTIFIER)('foo')
|
||||||
|
VALUE_PARAMETER_LIST
|
||||||
|
PsiElement(LPAR)('(')
|
||||||
|
PsiElement(RPAR)(')')
|
||||||
@@ -54,7 +54,7 @@ public class JetCodeConformanceTest extends TestCase {
|
|||||||
for (File sourceFile : FileUtil.findFilesByMask(JAVA_FILE_PATTERN, new File("compiler/frontend/src/org/jetbrains/jet/lang/parsing"))) {
|
for (File sourceFile : FileUtil.findFilesByMask(JAVA_FILE_PATTERN, new File("compiler/frontend/src/org/jetbrains/jet/lang/parsing"))) {
|
||||||
String source = FileUtil.loadFile(sourceFile, true);
|
String source = FileUtil.loadFile(sourceFile, true);
|
||||||
|
|
||||||
Pattern atPattern = Pattern.compile("assert.*?[^_]at.*?$", Pattern.MULTILINE);
|
Pattern atPattern = Pattern.compile("assert.*?\\b[^_]at.*?$", Pattern.MULTILINE);
|
||||||
Matcher matcher = atPattern.matcher(source);
|
Matcher matcher = atPattern.matcher(source);
|
||||||
|
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest {
|
|||||||
doParsingTest(fileName);
|
doParsingTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("DynamicReceiver.kt")
|
||||||
|
public void testDynamicReceiver() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DynamicReceiver.kt");
|
||||||
|
doParsingTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("DynamicSoftKeyword.kt")
|
@TestMetadata("DynamicSoftKeyword.kt")
|
||||||
public void testDynamicSoftKeyword() throws Exception {
|
public void testDynamicSoftKeyword() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DynamicSoftKeyword.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DynamicSoftKeyword.kt");
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ type
|
|||||||
|
|
||||||
"dynamic" is a *soft keyword*:
|
"dynamic" is a *soft keyword*:
|
||||||
- if it occurs in a non-type context, it's an identifier
|
- if it occurs in a non-type context, it's an identifier
|
||||||
- in a type context, when followed by a dot or an angle bracket `<`, it's an identifier
|
- in a type context, when followed by a dot (except for a dot that separates a receiver type from a function/property name) or an angle bracket `<`, it's an identifier
|
||||||
|
|
||||||
## Representation
|
## Representation
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user