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);
|
||||
|
||||
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();
|
||||
advance(); // DYNAMIC_KEYWORD
|
||||
dynamicType.done(DYNAMIC_TYPE);
|
||||
|
||||
+4
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.jet.lang.parsing;
|
||||
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface SemanticWhitespaceAwarePsiBuilder extends PsiBuilder {
|
||||
// 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 enableJoiningComplexTokens();
|
||||
void disableJoiningComplexTokens();
|
||||
|
||||
boolean isWhitespaceOrComment(@NotNull IElementType elementType);
|
||||
}
|
||||
|
||||
+7
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.jet.lang.parsing;
|
||||
|
||||
import com.intellij.lang.impl.PsiBuilderAdapter;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SemanticWhitespaceAwarePsiBuilderAdapter extends PsiBuilderAdapter implements SemanticWhitespaceAwarePsiBuilder {
|
||||
|
||||
@@ -62,4 +64,9 @@ public class SemanticWhitespaceAwarePsiBuilderAdapter extends PsiBuilderAdapter
|
||||
public void 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.impl.PsiBuilderAdapter;
|
||||
import com.intellij.lang.impl.PsiBuilderImpl;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
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 PsiBuilderImpl delegateImpl;
|
||||
|
||||
public SemanticWhitespaceAwarePsiBuilderImpl(PsiBuilder delegate) {
|
||||
super(delegate);
|
||||
newlinesEnabled.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
|
||||
|
||||
+32
-2
@@ -30,8 +30,7 @@ public class TruncatedSemanticWhitespaceAwarePsiBuilder extends SemanticWhitespa
|
||||
|
||||
@Override
|
||||
public boolean eof() {
|
||||
if (super.eof()) return true;
|
||||
return myEOFPosition >= 0 && getCurrentOffset() >= myEOFPosition;
|
||||
return super.eof() || isOffsetBeyondEof(getCurrentOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,4 +45,35 @@ public class TruncatedSemanticWhitespaceAwarePsiBuilder extends SemanticWhitespa
|
||||
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
|
||||
// FILE: k.kt
|
||||
|
||||
fun (dynamic).test() {
|
||||
fun dynamic.test() {
|
||||
val v1 = foo()
|
||||
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"))) {
|
||||
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);
|
||||
|
||||
if (matcher.find()) {
|
||||
|
||||
@@ -166,6 +166,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest {
|
||||
doParsingTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("DynamicReceiver.kt")
|
||||
public void testDynamicReceiver() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DynamicReceiver.kt");
|
||||
doParsingTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("DynamicSoftKeyword.kt")
|
||||
public void testDynamicSoftKeyword() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DynamicSoftKeyword.kt");
|
||||
|
||||
@@ -45,7 +45,7 @@ type
|
||||
|
||||
"dynamic" is a *soft keyword*:
|
||||
- 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user