Parsing dynamic in extension receiver position

This commit is contained in:
Andrey Breslav
2014-11-14 18:59:14 +03:00
parent 4f3158abb3
commit f58b30706e
11 changed files with 239 additions and 6 deletions
@@ -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);
@@ -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);
}
@@ -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);
}
}
@@ -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
@@ -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
+16
View File
@@ -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()
+140
View File
@@ -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");
+1 -1
View File
@@ -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