Improve check for statements-only postfix templates
Before this change the check was quite complicated
because of cases like:
for (i in 1..9)
foo(i)<caret>
It's not located in a block, but in the same time it's a stament.
So we had a tricky heuristics that if is parent is not a block, then
we should check if element isn't used as expression.
Of course this heuristics is wrong, e.g. for import/package nodes.
The solution is to reuse similar logic from BasicExpressionTypingVisitor.
it has been checked once that statement container is one of:
- KtBlockExpression
- KtContainerNodeForControlStructureBody
- KtWhenEntry
So there's no need to check anything else
#KT-14986 Fixed
#KT-14483 Fixed
This commit is contained in:
@@ -860,4 +860,14 @@ public class KtPsiUtil {
|
||||
}
|
||||
return deparenthesizedExpression;
|
||||
}
|
||||
|
||||
public static boolean isStatementContainer(@Nullable PsiElement container) {
|
||||
return container instanceof KtBlockExpression ||
|
||||
container instanceof KtContainerNodeForControlStructureBody ||
|
||||
container instanceof KtWhenEntry;
|
||||
}
|
||||
|
||||
public static boolean isStatement(@NotNull PsiElement element) {
|
||||
return isStatementContainer(element.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-7
@@ -1681,13 +1681,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
return isParentForBlockLevelExpression(parent);
|
||||
}
|
||||
|
||||
private static boolean isParentForBlockLevelExpression(@Nullable PsiElement parent) {
|
||||
return parent instanceof KtBlockExpression ||
|
||||
parent instanceof KtContainerNodeForControlStructureBody ||
|
||||
parent instanceof KtWhenEntry;
|
||||
return KtPsiUtil.isStatementContainer(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiverOrThis
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -145,8 +144,11 @@ private class KtExpressionPostfixTemplateSelector(
|
||||
val bindingContext by lazy { element.analyze(BodyResolveMode.PARTIAL) }
|
||||
|
||||
if (statementsOnly) {
|
||||
val elementToCheck = element.getQualifiedExpressionForReceiverOrThis()
|
||||
if (elementToCheck.parent !is KtBlockExpression && !elementToCheck.isUsedAsStatement(bindingContext)) return false
|
||||
// We use getQualifiedExpressionForReceiverOrThis because when postfix completion is run on some statement like:
|
||||
// foo().try<caret>
|
||||
// `element` points to `foo()` call, while we need to select the whole statement with `try` selector
|
||||
// to check if it's in a statement position
|
||||
if (!KtPsiUtil.isStatement(element.getQualifiedExpressionForReceiverOrThis())) return false
|
||||
}
|
||||
if (checkCanBeUsedAsValue && !element.canBeUsedAsValue()) return false
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo() {
|
||||
class A {
|
||||
// it's not a statement, while it has a block expression as an ancestor
|
||||
val w = 1.try<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo() {
|
||||
class A {
|
||||
// it's not a statement, while it has a block expression as an ancestor
|
||||
val w = 1.try <caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import qwe.try<caret>
|
||||
@@ -0,0 +1 @@
|
||||
import qwe.try <caret>
|
||||
+12
@@ -180,12 +180,24 @@ public class PostfixTemplateProviderTestGenerated extends AbstractPostfixTemplat
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryInLocalClass.kt")
|
||||
public void testTryInLocalClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/tryInLocalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryOnFor.kt")
|
||||
public void testTryOnFor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/tryOnFor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryOnImportStatement.kt")
|
||||
public void testTryOnImportStatement() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/tryOnImportStatement.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryOnStatement.kt")
|
||||
public void testTryOnStatement() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/tryOnStatement.kt");
|
||||
|
||||
Reference in New Issue
Block a user