mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[groovy] IDEA-208346 Highlight illegal single argument lambda
This commit is contained in:
@@ -102,6 +102,7 @@ change.modifier.not=Make ''{0}'' not {1}
|
||||
change.modifier.family.name=Change modifiers
|
||||
parameter.cast.fix=Cast {0,number,ordinal} parameter to {1}
|
||||
action.convert.lambda.to.closure=Convert lambda to closure
|
||||
add.parenthesis.to.lambda.parameter.list=Add parenthesis to parameter list
|
||||
# suppress inspection "UnusedProperty"
|
||||
packageLocal.visibility.presentation=default visible
|
||||
# suppress inspection "UnusedProperty"
|
||||
@@ -193,6 +194,7 @@ unsupported.negated.instanceof=Negated 'instanceof' is not supported in current
|
||||
unsupported.elvis.assignment=Elvis assignment is not supported in current version
|
||||
unsupported.safe.index.access=Safe index access is not supported in current version
|
||||
unsupported.lambda=Lambdas are not supported in current version
|
||||
illegal.single.argument.lambda=Single argument form of lambda is available only as right part of assignment expression or as argument inside method call
|
||||
|
||||
#Override and implement
|
||||
method.is.not.implemented=Method ''{0}'' is not implemented
|
||||
|
||||
+27
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.plugins.groovy.annotator
|
||||
|
||||
@@ -7,9 +7,16 @@ import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle
|
||||
import org.jetbrains.plugins.groovy.annotator.intentions.AddParenthesisToLambdaParameterAction
|
||||
import org.jetbrains.plugins.groovy.codeInspection.bugs.GrRemoveModifierFix
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrParenthesizedExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition
|
||||
|
||||
|
||||
@@ -31,5 +38,24 @@ class GroovyAnnotator30(private val holder: AnnotationHolder) : GroovyElementVis
|
||||
registerFix(annotation, GrRemoveModifierFix(PsiModifier.DEFAULT, GroovyBundle.message("illegal.default.modifier.fix")), modifier)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitLambdaExpression(expression: GrLambdaExpression) {
|
||||
checkSingleArgumentLambda(expression)
|
||||
super.visitLambdaExpression(expression)
|
||||
}
|
||||
|
||||
private fun checkSingleArgumentLambda(lambda: GrLambdaExpression) {
|
||||
val parameterList = lambda.parameterList
|
||||
parameterList.lParen?.let { return }
|
||||
val parent = lambda.parent
|
||||
when (parent) {
|
||||
is GrAssignmentExpression, is GrVariable, is GrParenthesizedExpression -> return
|
||||
is GrArgumentList -> if (parent.parent is GrMethodCallExpression) return
|
||||
}
|
||||
|
||||
holder.createErrorAnnotation(parameterList, GroovyBundle.message("illegal.single.argument.lambda")).apply {
|
||||
registerFix(AddParenthesisToLambdaParameterAction(lambda))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.annotator.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.psi.util.createSmartPointer
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle.message
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression
|
||||
|
||||
class AddParenthesisToLambdaParameterAction(parameterList: GrLambdaExpression) : IntentionAction {
|
||||
|
||||
private val myLambda: SmartPsiElementPointer<GrLambdaExpression> = parameterList.createSmartPointer()
|
||||
|
||||
override fun getText(): String = familyName
|
||||
|
||||
override fun getFamilyName(): String = message("add.parenthesis.to.lambda.parameter.list")
|
||||
|
||||
override fun startInWriteAction(): Boolean = true
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean {
|
||||
val parameterList = myLambda.element?.parameterList ?: return false
|
||||
parameterList.lParen ?: return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
|
||||
val lambda = myLambda.element ?: return
|
||||
val closureText = closureText(lambda) ?: return
|
||||
val closure = GroovyPsiElementFactory.getInstance(project).createLambdaFromText(closureText)
|
||||
lambda.replaceWithExpression(closure, false)
|
||||
}
|
||||
|
||||
private fun closureText(lambda: GrLambdaExpression): String? {
|
||||
val closureText = StringBuilder()
|
||||
closureText.append("(")
|
||||
val parameterList = lambda.parameterList
|
||||
appendTextBetween(closureText, parameterList.text, parameterList.lParen, parameterList.rParen)
|
||||
closureText.append(")")
|
||||
appendTextBetween(closureText, lambda.text, parameterList, null)
|
||||
|
||||
return closureText.toString()
|
||||
}
|
||||
}
|
||||
-22
@@ -4,7 +4,6 @@ package org.jetbrains.plugins.groovy.annotator.intentions
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.psi.util.createSmartPointer
|
||||
@@ -51,25 +50,4 @@ class ConvertLambdaToClosureAction(lambda: GrLambdaExpression) : IntentionAction
|
||||
closureText.append("}")
|
||||
return closureText.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends [text] to the [builder] cutting length of [left] text from the start and length of [right] text from the end.
|
||||
*/
|
||||
private fun appendTextBetween(builder: StringBuilder, text: String, left: PsiElement?, right: PsiElement?) {
|
||||
val start = left?.textLength ?: 0
|
||||
val end = text.length - (right?.textLength ?: 0)
|
||||
builder.append(text, start, end)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends text of elements to the [builder] between [start] and [stop].
|
||||
* If [stop] is `null` then all siblings of [start] are processed.
|
||||
*/
|
||||
private fun appendElements(builder: StringBuilder, start: PsiElement, stop: PsiElement) {
|
||||
var current: PsiElement? = start.nextSibling
|
||||
while (current !== null && current !== stop) {
|
||||
builder.append(current.text)
|
||||
current = current.nextSibling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.annotator.intentions
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
/**
|
||||
* Appends [text] to the [builder] cutting length of [left] text from the start and length of [right] text from the end.
|
||||
*/
|
||||
internal fun appendTextBetween(builder: StringBuilder, text: String, left: PsiElement?, right: PsiElement?) {
|
||||
val start = left?.textLength ?: 0
|
||||
val end = text.length - (right?.textLength ?: 0)
|
||||
builder.append(text, start, end)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends text of elements to the [builder] between [start] and [stop].
|
||||
* If [stop] is `null` then all siblings of [start] are processed.
|
||||
*/
|
||||
internal fun appendElements(builder: StringBuilder, start: PsiElement, stop: PsiElement) {
|
||||
var current: PsiElement? = start.nextSibling
|
||||
while (current !== null && current !== stop) {
|
||||
builder.append(current.text)
|
||||
current = current.nextSibling
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.lang.psi;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocMemberReference;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair;
|
||||
@@ -187,6 +188,14 @@ public abstract class GroovyPsiElementFactory implements JVMElementFactory {
|
||||
return createClosureFromText(s, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract GrLambdaExpression createLambdaFromText(@NotNull String text, @Nullable PsiElement context);
|
||||
|
||||
@NotNull
|
||||
public GrLambdaExpression createLambdaFromText(@NotNull String text) {
|
||||
return createLambdaFromText(text, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GrParameter createParameter(@NotNull String name, @Nullable String typeText, @Nullable GroovyPsiElement context) throws IncorrectOperationException {
|
||||
return createParameter(name, typeText, null, context);
|
||||
|
||||
+13
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.lang.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocMemberReference;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocTag;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
|
||||
@@ -361,6 +362,17 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory {
|
||||
return ((GrClosableBlock)initializer);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public GrLambdaExpression createLambdaFromText(@NotNull String lambdaText, PsiElement context) throws IncorrectOperationException {
|
||||
GroovyFile psiFile = createGroovyFileChecked("def __hdsjfghkl_sdhjfshglk_foo = " + lambdaText, false, context);
|
||||
final GrStatement st = psiFile.getStatements()[0];
|
||||
LOG.assertTrue(st instanceof GrVariableDeclaration, lambdaText);
|
||||
final GrExpression initializer = ((GrVariableDeclaration)st).getVariables()[0].getInitializerGroovy();
|
||||
LOG.assertTrue(initializer instanceof GrLambdaExpression, lambdaText);
|
||||
return ((GrLambdaExpression)initializer);
|
||||
}
|
||||
|
||||
private GroovyFileImpl createDummyFile(@NotNull CharSequence text, boolean physical) {
|
||||
final String fileName = DUMMY_FILE_NAME + '.' + GroovyFileType.GROOVY_FILE_TYPE.getDefaultExtension();
|
||||
final long stamp = System.currentTimeMillis();
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.lang.highlighting
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle
|
||||
import org.jetbrains.plugins.groovy.util.ActionTest
|
||||
import org.jetbrains.plugins.groovy.util.Groovy30Test
|
||||
import org.junit.Test
|
||||
|
||||
@CompileStatic
|
||||
class AddParenthesisToLambdaParameterTest extends Groovy30Test implements ActionTest {
|
||||
|
||||
private void doTest(String before, String after) {
|
||||
doActionTest(GroovyBundle.message("add.parenthesis.to.lambda.parameter.list"), before, after)
|
||||
}
|
||||
|
||||
@Test
|
||||
void 'statement expression'() {
|
||||
doTest '<caret>a -> /*mark*/ b', '(a) -> /*mark*/ b'
|
||||
}
|
||||
|
||||
@Test
|
||||
void 'nested lambda'() {
|
||||
doTest 'a -> <caret>b /*mark*/-> c', 'a -> (b) /*mark*/ -> c'
|
||||
}
|
||||
|
||||
@Test
|
||||
void 'multyline'() {
|
||||
doTest '''
|
||||
true ? a<caret>a -> {
|
||||
//line comment
|
||||
aa++
|
||||
} : () -> {}
|
||||
''', '''
|
||||
true ? (aa) -> {
|
||||
//line comment
|
||||
aa++
|
||||
} : () -> {}
|
||||
'''
|
||||
}
|
||||
}
|
||||
+4
@@ -76,4 +76,8 @@ I i = {3}
|
||||
void 'test reassigned var in lambda 2'() {
|
||||
highlightingTest GrUnresolvedAccessInspection
|
||||
}
|
||||
|
||||
void 'test illegal single argument lambda'() {
|
||||
highlightingTest ()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.util
|
||||
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.plugins.groovy.GroovyProjectDescriptors
|
||||
|
||||
@CompileStatic
|
||||
abstract class Groovy30Test extends LightProjectTest {
|
||||
|
||||
@Override
|
||||
final LightProjectDescriptor getProjectDescriptor() {
|
||||
GroovyProjectDescriptors.GROOVY_3_0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<error descr="Single argument form of lambda is available only as right part of assignment expression or argument inside method call">a</error> -> a
|
||||
def l = b-> <error descr="Single argument form of lambda is available only as right part of assignment expression or argument inside method call">c</error>-> d
|
||||
def l2 = false ? <error descr="Single argument form of lambda is available only as right part of assignment expression or argument inside method call">e</error>->e : <error descr="Single argument form of lambda is available only as right part of assignment expression or argument inside method call">f</error>->f
|
||||
if ((e->e) != null){}
|
||||
m(g->g)
|
||||
def l3 = h->h
|
||||
l3 = j->j
|
||||
Reference in New Issue
Block a user