mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
'cast parameter to type' fixes in Groovy
This commit is contained in:
+6
-3
@@ -41,16 +41,19 @@ public class GrCastFix extends GroovyFix implements LocalQuickFix {
|
||||
|
||||
@Override
|
||||
protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
|
||||
if (!myExpectedType.isValid()) return;
|
||||
doCast(project, myExpectedType, descriptor.getPsiElement());
|
||||
}
|
||||
|
||||
static void doCast(Project project, PsiType type, PsiElement element) {
|
||||
if (!type.isValid()) return;
|
||||
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (!(element instanceof GrExpression)) return;
|
||||
|
||||
final GrExpression expr = (GrExpression)element;
|
||||
|
||||
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project);
|
||||
final GrSafeCastExpression cast = (GrSafeCastExpression)factory.createExpressionFromText("foo as String");
|
||||
final GrTypeElement typeElement = factory.createTypeElement(myExpectedType);
|
||||
final GrTypeElement typeElement = factory.createTypeElement(type);
|
||||
cast.getOperand().replaceWithExpression(expr, true);
|
||||
cast.getCastTypeElement().replace(typeElement);
|
||||
|
||||
|
||||
+42
-13
@@ -19,9 +19,11 @@ package org.jetbrains.plugins.groovy.codeInspection.assignment;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.Function;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -41,6 +43,8 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
|
||||
@@ -67,6 +71,8 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.MixinMemberContributor;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -110,16 +116,16 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
|
||||
private static class MyVisitor extends BaseInspectionVisitor {
|
||||
private void checkAssignability(@NotNull PsiType expectedType, @NotNull GrExpression expression, GroovyPsiElement element) {
|
||||
private void checkAssignability(@NotNull PsiType expectedType, @NotNull GrExpression expression) {
|
||||
if (PsiUtil.isRawClassMemberAccess(expression)) return; //GRVY-2197
|
||||
if (checkForImplicitEnumAssigning(expectedType, expression, element)) return;
|
||||
if (checkForImplicitEnumAssigning(expectedType, expression, expression)) return;
|
||||
final PsiType rType = expression.getType();
|
||||
if (rType == null || rType == PsiType.VOID) return;
|
||||
|
||||
if (!TypesUtil.isAssignable(expectedType, rType, element)) {
|
||||
if (!TypesUtil.isAssignable(expectedType, rType, expression)) {
|
||||
final LocalQuickFix[] fixes = {new GrCastFix(expectedType)};
|
||||
final String message = GroovyBundle.message("cannot.assign", rType.getPresentableText(), expectedType.getPresentableText());
|
||||
registerError(element, message, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
registerError(expression, message, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +172,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
if (returnValue != null &&
|
||||
!(returnValue.getParent() instanceof GrReturnStatement) &&
|
||||
!isNewInstanceInitialingByTuple(returnValue)) {
|
||||
checkAssignability(expectedType, returnValue, returnValue);
|
||||
checkAssignability(expectedType, returnValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -189,7 +195,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
//don't check if the return type is void. the check is done inside annotator, because it's a compilation error
|
||||
if (expectedType == PsiType.VOID) return;
|
||||
|
||||
checkAssignability(expectedType, value, returnStatement);
|
||||
checkAssignability(expectedType, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,7 +221,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
if (clazz != null && CommonClassNames.JAVA_UTIL_LIST.equals(clazz.getQualifiedName())) {
|
||||
final PsiType[] types = pct.getParameters();
|
||||
if (types.length == 1 && types[0] != null && rType != null) {
|
||||
checkAssignability(types[0], rValue, rValue);
|
||||
checkAssignability(types[0], rValue);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -231,7 +237,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
|
||||
if (lType != null && rType != null) {
|
||||
checkAssignability(lType, rValue, rValue);
|
||||
checkAssignability(lType, rValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +255,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
return;
|
||||
}
|
||||
|
||||
checkAssignability(varType, initializer, initializer);
|
||||
checkAssignability(varType, initializer);
|
||||
}
|
||||
|
||||
private static boolean isNewInstanceInitialingByTuple(GrExpression initializer) {
|
||||
@@ -363,8 +369,8 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
|
||||
final GrExpression exception = throwStatement.getException();
|
||||
if (exception != null) {
|
||||
checkAssignability(PsiType.getJavaLangThrowable(throwStatement.getManager(), throwStatement.getResolveScope()), exception,
|
||||
exception);
|
||||
checkAssignability(PsiType.getJavaLangThrowable(throwStatement.getManager(), throwStatement.getResolveScope()), exception
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,7 +548,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
|
||||
private void highlightInapplicableMethodUsage(GroovyResolveResult methodResolveResult,
|
||||
PsiElement place,
|
||||
GroovyPsiElement place,
|
||||
PsiMethod method,
|
||||
PsiType[] argumentTypes) {
|
||||
final PsiClass containingClass =
|
||||
@@ -563,9 +569,32 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
else {
|
||||
message = GroovyBundle.message("cannot.apply.method1", method.getName(), canonicalText, typesString);
|
||||
}
|
||||
registerError(getElementToHighlight(place, PsiUtil.getArgumentsList(place)), message);
|
||||
|
||||
registerError(getElementToHighlight(place, PsiUtil.getArgumentsList(place)), message,
|
||||
genCastFixes(GrClosureSignatureUtil.createSignature(methodResolveResult), argumentTypes, place),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
|
||||
private static LocalQuickFix[] genCastFixes(GrSignature signature, PsiType[] argumentTypes, GroovyPsiElement context) {
|
||||
|
||||
final List<GrClosureSignature> signatures = GrClosureSignatureUtil.generateSimpleSignature(signature);
|
||||
|
||||
List<Pair<Integer, PsiType>> errors = new ArrayList<Pair<Integer, PsiType>>();
|
||||
for (GrClosureSignature closureSignature : signatures) {
|
||||
final GrClosureSignatureUtil.MapResultWithError<PsiType> map =
|
||||
GrClosureSignatureUtil.mapSimpleSignatureWithErrors(closureSignature, argumentTypes, Function.ID, context, 1);
|
||||
if (map != null) {
|
||||
errors.addAll(map.getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>();
|
||||
for (Pair<Integer, PsiType> error : errors) {
|
||||
fixes.add(new ParameterCastFix(error.first, error.second));
|
||||
}
|
||||
|
||||
return fixes.toArray(new LocalQuickFix[fixes.size()]);
|
||||
}
|
||||
|
||||
private boolean checkCallApplicability(PsiType type, GroovyPsiElement invokedExpr, boolean checkUnknownArgs) {
|
||||
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.codeInspection.assignment;
|
||||
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovyFix;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
public class ParameterCastFix extends GroovyFix {
|
||||
private final int param;
|
||||
private final PsiType myType;
|
||||
private String myName;
|
||||
|
||||
public ParameterCastFix(int param, PsiType type) {
|
||||
this.param = param;
|
||||
myType = type;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Cast ");
|
||||
|
||||
builder.append(param + 1);
|
||||
switch (param + 1) {
|
||||
case 1:
|
||||
builder.append("st");
|
||||
break;
|
||||
case 2:
|
||||
builder.append("nd");
|
||||
break;
|
||||
case 3:
|
||||
builder.append("rd");
|
||||
break;
|
||||
default:
|
||||
builder.append("th");
|
||||
break;
|
||||
}
|
||||
builder.append(" parameter to ").append(type.getCanonicalText());
|
||||
|
||||
|
||||
myName = builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
final GrArgumentList list = element instanceof GrArgumentList ? (GrArgumentList)element :PsiUtil.getArgumentsList(element);
|
||||
if (list == null) return;
|
||||
|
||||
final GrExpression[] arguments = list.getExpressionArguments();
|
||||
|
||||
final int p = list.getNamedArguments().length > 0 ? param - 1 : param;
|
||||
if (arguments.length <= p) return;
|
||||
|
||||
GrCastFix.doCast(project, myType, arguments[p]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
}
|
||||
+90
@@ -847,4 +847,94 @@ public class GrClosureSignatureUtil {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static class MapResultWithError<Arg> {
|
||||
private final ArgInfo<Arg>[] mapping;
|
||||
private final List<Pair<Integer, PsiType>> errorsAndExpectedType;
|
||||
|
||||
public MapResultWithError(ArgInfo<Arg>[] mapping, List<Pair<Integer, PsiType>> errorsAndExpectedType) {
|
||||
this.mapping = mapping;
|
||||
this.errorsAndExpectedType = errorsAndExpectedType;
|
||||
}
|
||||
|
||||
public ArgInfo<Arg>[] getMapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public List<Pair<Integer, PsiType>> getErrors() {
|
||||
return errorsAndExpectedType;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <Arg> MapResultWithError<Arg> mapSimpleSignatureWithErrors(@NotNull GrClosureSignature signature,
|
||||
@NotNull Arg[] args,
|
||||
@NotNull Function<Arg, PsiType> typeComputer,
|
||||
@NotNull GroovyPsiElement context,
|
||||
int maxErrorCount) {
|
||||
final GrClosureParameter[] params = signature.getParameters();
|
||||
if (args.length < params.length) return null;
|
||||
|
||||
if (args.length > params.length && !signature.isVarargs()) return null;
|
||||
|
||||
int optional = getOptionalParamCount(params, false);
|
||||
assert optional == 0;
|
||||
|
||||
int errorCount = 0;
|
||||
ArgInfo<Arg>[] map = new ArgInfo[params.length];
|
||||
|
||||
List<Pair<Integer, PsiType>> errors = new ArrayList<Pair<Integer, PsiType>>(maxErrorCount);
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (isAssignableByConversion(params[i].getType(), typeComputer.fun(args[i]), context)) {
|
||||
map[i] = new ArgInfo<Arg>(args[i]);
|
||||
}
|
||||
else if (params[i].getType() instanceof PsiArrayType && i == params.length - 1) {
|
||||
if (i + 1 == args.length) {
|
||||
errors.add(new Pair<Integer, PsiType>(i, params[i].getType()));
|
||||
}
|
||||
final PsiType ellipsis = ((PsiArrayType)params[i].getType()).getComponentType();
|
||||
for (int j = i; j < args.length; j++) {
|
||||
if (!isAssignableByConversion(ellipsis, typeComputer.fun(args[j]), context)) {
|
||||
errorCount++;
|
||||
if (errorCount > maxErrorCount) return null;
|
||||
errors.add(new Pair<Integer, PsiType>(i, ellipsis));
|
||||
}
|
||||
map[i] = new ArgInfo<Arg>(args[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
errorCount++;
|
||||
if (errorCount > maxErrorCount) return null;
|
||||
errors.add(new Pair<Integer, PsiType>(i, params[i].getType()));
|
||||
}
|
||||
}
|
||||
return new MapResultWithError<Arg>(map, errors);
|
||||
}
|
||||
|
||||
public static List<GrClosureSignature> generateSimpleSignature(GrSignature signature) {
|
||||
final List<GrClosureSignature> result = new ArrayList<GrClosureSignature>();
|
||||
signature.accept(new GrRecursiveSignatureVisitor() {
|
||||
@Override
|
||||
public void visitClosureSignature(GrClosureSignature signature) {
|
||||
final GrClosureParameter[] original = signature.getParameters();
|
||||
final ArrayList<GrClosureParameter> parameters = new ArrayList<GrClosureParameter>(original.length);
|
||||
|
||||
for (GrClosureParameter parameter : original) {
|
||||
parameters.add(new GrClosureParameterImpl(parameter.getType(), false, null));
|
||||
}
|
||||
|
||||
final int pcount = signature.isVarargs() ? signature.getParameterCount() - 2 : signature.getParameterCount() - 1;
|
||||
for (int i = pcount; i >= 0; i--) {
|
||||
if (original[i].isOptional()) {
|
||||
result.add(new GrClosureSignatureImpl(parameters.toArray(new GrClosureParameter[parameters.size()]), signature.getReturnType(), signature.isVarargs(), false));
|
||||
parameters.remove(i);
|
||||
}
|
||||
}
|
||||
result.add(new GrClosureSignatureImpl(parameters.toArray(new GrClosureParameter[parameters.size()]), signature.getReturnType(), signature.isVarargs(), false));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.intentions
|
||||
|
||||
import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection
|
||||
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
class ArgumentCastTest extends GrIntentionTestCase {
|
||||
void test0() {
|
||||
doTextTest('''\
|
||||
def foo(char c) {}
|
||||
foo(<caret>'a')
|
||||
''', "Cast 1st parameter to char", '''\
|
||||
def foo(char c) {}
|
||||
foo('a' as char)
|
||||
''', GroovyAssignabilityCheckInspection)
|
||||
}
|
||||
|
||||
void test1() {
|
||||
doTextTest('''\
|
||||
def foo(char c, int x) {}
|
||||
foo(<caret>'a', 2)
|
||||
''', "Cast 1st parameter to char", '''\
|
||||
def foo(char c, int x) {}
|
||||
foo('a' as char, 2)
|
||||
''', GroovyAssignabilityCheckInspection)
|
||||
}
|
||||
|
||||
void test2() {
|
||||
doAntiTest('''\
|
||||
def foo(char c, int x) {}
|
||||
|
||||
foo(<caret>'a', 2, 3)
|
||||
''', "Cast 1st parameter to char", GroovyAssignabilityCheckInspection)
|
||||
}
|
||||
|
||||
void test3() {
|
||||
doAntiTest('''\
|
||||
def foo(char c, int x) {}
|
||||
|
||||
foo(<caret>'a', 'a')
|
||||
''', "Cast 1st parameter to char", GroovyAssignabilityCheckInspection)
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.intentions;
|
||||
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
|
||||
@@ -44,16 +45,18 @@ public abstract class GrIntentionTestCase extends LightCodeInsightFixtureTestCas
|
||||
}
|
||||
}
|
||||
|
||||
protected void doTextTest(String before, String hint, String after) {
|
||||
protected void doTextTest(String before, String hint, String after, Class<? extends LocalInspectionTool>... inspections) {
|
||||
myFixture.configureByText("a.groovy", before);
|
||||
myFixture.enableInspections(inspections)
|
||||
final List<IntentionAction> list = myFixture.filterAvailableIntentions(hint);
|
||||
myFixture.launchAction(assertOneElement(list));
|
||||
PostprocessReformattingAspect.getInstance(project).doPostponedFormatting();
|
||||
myFixture.checkResult(after);
|
||||
}
|
||||
|
||||
protected void doAntiTest(String before, String hint) {
|
||||
protected void doAntiTest(String before, String hint, Class<? extends LocalInspectionTool>... inspections) {
|
||||
myFixture.configureByText("a.groovy", before);
|
||||
myFixture.enableInspections(inspections)
|
||||
assertEmpty(myFixture.filterAvailableIntentions(hint));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class X{
|
||||
int method1(Date date) {
|
||||
<warning descr="Cannot assign 'Date' to 'int'">return date</warning>;
|
||||
return <warning descr="Cannot assign 'Date' to 'int'">date</warning>;
|
||||
}
|
||||
|
||||
int method2(Date date) {
|
||||
|
||||
@@ -3,7 +3,7 @@ File foo() {
|
||||
if (ints.empty) {
|
||||
print {return 42}
|
||||
for (x in ints) {
|
||||
<warning descr="Cannot assign 'Integer' to 'File'">return 43</warning>
|
||||
return <warning descr="Cannot assign 'Integer' to 'File'">43</warning>
|
||||
}
|
||||
}
|
||||
<warning descr="Cannot assign 'Integer' to 'File'">67</warning>
|
||||
|
||||
Reference in New Issue
Block a user