Reserve "async* {}", extend the quick-fix

This commit is contained in:
Andrey Breslav
2015-12-21 06:52:49 +03:00
parent 45074841a4
commit a7e7d53e2b
13 changed files with 102 additions and 8 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.cfg; package org.jetbrains.kotlin.cfg;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
@@ -285,6 +286,6 @@ public final class WhenChecker {
} }
public static void checkReservedPrefix(@NotNull BindingTrace trace, @NotNull KtWhenExpression expression) { public static void checkReservedPrefix(@NotNull BindingTrace trace, @NotNull KtWhenExpression expression) {
KtPsiUtilKt.checkReservedPrefixWord(trace, expression.getWhenKeyword(), "sealed", "sealed when"); KtPsiUtilKt.checkReservedPrefixWord(trace, expression.getWhenKeyword(), "sealed", TokenSet.EMPTY, "sealed when");
} }
} }
@@ -241,11 +241,15 @@ public interface KtTokens {
TokenSet OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, DOT, PLUSPLUS, MINUSMINUS, EXCLEXCL, MUL, PLUS, TokenSet OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, DOT, PLUSPLUS, MINUSMINUS, EXCLEXCL, MUL, PLUS,
MINUS, EXCL, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR, MINUS, EXCL, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR,
SAFE_ACCESS, ELVIS, SAFE_ACCESS, ELVIS,
// MAP, FILTER,
RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ,
NOT_IN, NOT_IS, NOT_IN, NOT_IS,
IDENTIFIER); IDENTIFIER);
TokenSet BINARY_OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, MUL, PLUS,
MINUS, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR,
ELVIS, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ,
NOT_IN, NOT_IS);
TokenSet AUGMENTED_ASSIGNMENTS = TokenSet.create(PLUSEQ, MINUSEQ, MULTEQ, PERCEQ, DIVEQ); TokenSet AUGMENTED_ASSIGNMENTS = TokenSet.create(PLUSEQ, MINUSEQ, MULTEQ, PERCEQ, DIVEQ);
TokenSet ALL_ASSIGNMENTS = TokenSet.create(EQ, PLUSEQ, MINUSEQ, MULTEQ, PERCEQ, DIVEQ); TokenSet ALL_ASSIGNMENTS = TokenSet.create(EQ, PLUSEQ, MINUSEQ, MULTEQ, PERCEQ, DIVEQ);
} }
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.codeInsight.CommentUtilCore; import com.intellij.util.codeInsight.CommentUtilCore;
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
@@ -608,9 +609,21 @@ public class KtPsiUtil {
return prev; return prev;
} }
/**
* Example:
* code: async* {}
* element = "{}"
* word = "async"
* suffixTokens = [+, -, *, /, %]
*
* result = async
*/
@Nullable @Nullable
public static PsiElement getPreviousWord(@NotNull PsiElement element, @NotNull String word) { public static PsiElement getPreviousWord(@NotNull PsiElement element, @NotNull String word, @NotNull TokenSet suffixTokens) {
PsiElement prev = prevLeafIgnoringWhitespaceAndComments(element); PsiElement prev = prevLeafIgnoringWhitespaceAndComments(element);
if (prev != null && suffixTokens.contains(prev.getNode().getElementType())) {
prev = PsiTreeUtil.prevLeaf(prev, false);
}
if (prev != null && prev.getNode().getElementType() == KtTokens.IDENTIFIER && word.equals(prev.getText())) { if (prev != null && prev.getNode().getElementType() == KtTokens.IDENTIFIER && word.equals(prev.getText())) {
return prev; return prev;
} }
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiParameter
import com.intellij.psi.PsiParameterList import com.intellij.psi.PsiParameterList
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubElement
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.DiagnosticSink
@@ -434,8 +435,8 @@ fun canPlaceAfterSimpleNameEntry(element: PsiElement?): Boolean {
return !BAD_NEIGHBOUR_FOR_SIMPLE_TEMPLATE_ENTRY_PATTERN.matches(entryText) return !BAD_NEIGHBOUR_FOR_SIMPLE_TEMPLATE_ENTRY_PATTERN.matches(entryText)
} }
fun checkReservedPrefixWord(sink: DiagnosticSink, element: PsiElement, word: String, message: String) { fun checkReservedPrefixWord(sink: DiagnosticSink, element: PsiElement, word: String, suffixTokens: TokenSet, message: String) {
KtPsiUtil.getPreviousWord(element, word)?.let { KtPsiUtil.getPreviousWord(element, word, suffixTokens)?.let {
sink.report(Errors.UNSUPPORTED.on(it, message)) sink.report(Errors.UNSUPPORTED.on(it, message))
} }
} }
@@ -159,7 +159,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre
} }
private fun checkReservedAsync(context: ExpressionTypingContext, expression: PsiElement) { private fun checkReservedAsync(context: ExpressionTypingContext, expression: PsiElement) {
checkReservedPrefixWord(context.trace, expression, "async", "async block/lambda. Use 'async() { ... }' or 'async(fun...)'") checkReservedPrefixWord(context.trace, expression, "async", KtTokens.BINARY_OPERATIONS, "async block/lambda. Use 'async() { ... }' or 'async(fun...)'")
} }
private fun createFunctionLiteralDescriptor( private fun createFunctionLiteralDescriptor(
+22
View File
@@ -21,3 +21,25 @@ fun test(foo: Any) {
async (fun () {}) async (fun () {})
} }
object async {
operator fun plus(f: () -> Unit) = f()
operator fun minus(f: () -> Unit) = f()
operator fun times(f: () -> Unit) = f()
operator fun div(f: () -> Unit) = f()
operator fun mod(f: () -> Unit) = f()
}
fun test() {
<!UNSUPPORTED!>async<!>+ {}
<!UNSUPPORTED!>async<!>- {}
<!UNSUPPORTED!>async<!>* {}
<!UNSUPPORTED!>async<!>/ {}
<!UNSUPPORTED!>async<!>% {}
async + {}
async - {}
async * {}
async / {}
async % {}
}
+13
View File
@@ -1,5 +1,18 @@
package package
public fun async(/*0*/ f: () -> kotlin.Unit): kotlin.Unit public fun async(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public fun test(): kotlin.Unit
public fun test(/*0*/ foo: kotlin.Any): kotlin.Unit public fun test(/*0*/ foo: kotlin.Any): kotlin.Unit
public infix fun kotlin.Any.async(/*0*/ f: () -> kotlin.Unit): kotlin.Unit public infix fun kotlin.Any.async(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public object async {
private constructor async()
public final operator fun div(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final operator fun minus(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public final operator fun mod(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public final operator fun plus(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public final operator fun times(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.expression.KotlinParenthesesSurrounder import org.jetbrains.kotlin.idea.codeInsight.surroundWith.expression.KotlinParenthesesSurrounder
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -33,8 +34,15 @@ public class UnsupportedAsyncFix(val psiElement: PsiElement): KotlinQuickFixActi
override fun getText(): String = familyName override fun getText(): String = familyName
override fun invoke(project: Project, editor: Editor?, file: KtFile) { override fun invoke(project: Project, editor: Editor?, file: KtFile) {
if (element is KtBinaryExpression && element.right != null) { if (element is KtBinaryExpression) {
KotlinParenthesesSurrounder.surroundWithParentheses(element.right!!) if (element.operationToken != KtTokens.IDENTIFIER) {
// async+ {}
element.addBefore(KtPsiFactory(element).createWhiteSpace(), element.operationReference)
}
else if (element.right != null) {
// foo async {}
KotlinParenthesesSurrounder.surroundWithParentheses(element.right!!)
}
} }
if (element is KtCallExpression) { if (element is KtCallExpression) {
+5
View File
@@ -79,6 +79,9 @@ fun infixTest() {
fun async(f: () -> Unit) {} fun async(f: () -> Unit) {}
infix fun Any.async(f: () -> Unit) {} infix fun Any.async(f: () -> Unit) {}
object async {
operator fun times(f: () -> Unit) = f()
}
fun test(foo: Any) { fun test(foo: Any) {
async { } async { }
@@ -94,5 +97,7 @@ fun test(foo: Any) {
foo async (fun () {}) foo async (fun () {})
async (fun () {}) async (fun () {})
async* {}
} }
+5
View File
@@ -78,6 +78,9 @@ fun infixTest() {
fun async(f: () -> Unit) {} fun async(f: () -> Unit) {}
infix fun Any.async(f: () -> Unit) {} infix fun Any.async(f: () -> Unit) {}
object async {
operator fun times(f: () -> Unit) = f()
}
fun test(foo: Any) { fun test(foo: Any) {
async() { } async() { }
@@ -93,5 +96,7 @@ fun test(foo: Any) {
foo async (fun () {}) foo async (fun () {})
async (fun () {}) async (fun () {})
async * {}
} }
@@ -0,0 +1,8 @@
// "Migrate unsupported async syntax" "true"
object async {
operator fun times(f: () -> Unit) = f()
}
fun test() {
asy<caret>nc* { }
}
@@ -0,0 +1,8 @@
// "Migrate unsupported async syntax" "true"
object async {
operator fun times(f: () -> Unit) = f()
}
fun test() {
asy<caret>nc * { }
}
@@ -540,6 +540,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/asyncUnsupported/asyncWithLambdaAndComment.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/asyncUnsupported/asyncWithLambdaAndComment.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("asyncWithTimes.kt")
public void testAsyncWithTimes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/asyncUnsupported/asyncWithTimes.kt");
doTest(fileName);
}
} }
@TestMetadata("idea/testData/quickfix/autoImports") @TestMetadata("idea/testData/quickfix/autoImports")