mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
check for qualifier type on category method call
This commit is contained in:
+1
@@ -84,3 +84,4 @@ remove.0=Remove {0}
|
||||
replace.postfix.0.with.prefix.0=Replace postfix {0} with prefix {0}
|
||||
replace.0.with.1=Replace {0} with binary {1}
|
||||
gr.deprecated.api.usage=Deprecated API inspection
|
||||
category,method.0.cannot.be.applied.to.1=Category method ''{0}'' cannot be applied to ''{1}''
|
||||
|
||||
+56
-8
@@ -30,12 +30,14 @@ import org.jetbrains.plugins.groovy.annotator.GroovyAnnotator;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovyFix;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
|
||||
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
|
||||
import org.jetbrains.plugins.groovy.extensions.GroovyNamedArgumentProvider;
|
||||
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor;
|
||||
import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
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;
|
||||
@@ -55,12 +57,14 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
|
||||
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.Map;
|
||||
@@ -178,7 +182,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
|
||||
final GrExpression value = returnStatement.getReturnValue();
|
||||
if (isNewInstanceInitialingByTuple(value)) return;
|
||||
|
||||
|
||||
final PsiType expectedType = method.getReturnType();
|
||||
if (value == null || expectedType == null) return;
|
||||
checkAssignability(expectedType, value, returnStatement);
|
||||
@@ -213,7 +217,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
return;
|
||||
}
|
||||
if (lValue instanceof GrReferenceExpression && ((GrReferenceExpression)lValue).resolve() instanceof GrReferenceExpression) {
|
||||
//lvalue is not-declared variable
|
||||
//lvalue is not-declared variable
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -296,7 +300,6 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +338,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
registerError(listOrMap, GroovyBundle.message("cannot.apply.default.constructor", ((PsiClass)element).getName()));
|
||||
}
|
||||
}
|
||||
else if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()){
|
||||
else if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) {
|
||||
checkLiteralConstructorApplicability(result, listOrMap, true);
|
||||
}
|
||||
}
|
||||
@@ -356,7 +359,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,
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +403,9 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkConstructorApplicability(GroovyResolveResult constructorResolveResult, GroovyPsiElement place, boolean checkUnknownArgs) {
|
||||
private boolean checkConstructorApplicability(GroovyResolveResult constructorResolveResult,
|
||||
GroovyPsiElement place,
|
||||
boolean checkUnknownArgs) {
|
||||
final PsiElement element = constructorResolveResult.getElement();
|
||||
LOG.assertTrue(element instanceof PsiMethod && ((PsiMethod)element).isConstructor());
|
||||
final PsiMethod constructor = (PsiMethod)element;
|
||||
@@ -408,7 +414,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
if (argList != null) {
|
||||
final GrExpression[] exprArgs = argList.getExpressionArguments();
|
||||
|
||||
if (exprArgs.length == 0 && !PsiUtil.isConstructorHasRequiredParameters(constructor)) return true;
|
||||
if (exprArgs.length == 0 && !PsiUtil.isConstructorHasRequiredParameters(constructor)) return true;
|
||||
}
|
||||
return checkMethodApplicability(constructorResolveResult, place, checkUnknownArgs);
|
||||
}
|
||||
@@ -515,7 +521,8 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
expressionType = TypesUtil.boxPrimitiveType(expressionType, call.getManager(), call.getResolveScope());
|
||||
|
||||
if (!descriptor.checkType(expressionType, call)) {
|
||||
registerError(namedArgumentExpression, "Type of argument '" + labelName + "' can not be '" + expressionType.getPresentableText() + "'");
|
||||
registerError(namedArgumentExpression,
|
||||
"Type of argument '" + labelName + "' can not be '" + expressionType.getPresentableText() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,6 +652,18 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (method instanceof GrGdkMethod && place instanceof GrReferenceExpression) {
|
||||
final PsiMethod staticMethod = ((GrGdkMethod)method).getStaticMethod();
|
||||
final PsiType qualifier = inferQualifierTypeByPlace((GrReferenceExpression)place);
|
||||
if (qualifier != null && !MixinMemberContributor.isCategoryMethod(staticMethod, qualifier, methodResolveResult.getSubstitutor())) {
|
||||
registerError(((GrReferenceExpression)place).getReferenceNameElement(),
|
||||
GroovyInspectionBundle.message("category,method.0.cannot.be.applied.to.1", method.getName(),
|
||||
qualifier.getCanonicalText()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (argumentTypes == null) return true;
|
||||
|
||||
GrClosureSignatureUtil.ApplicabilityResult applicable = PsiUtil.isApplicableConcrete(argumentTypes, method, methodResolveResult.getSubstitutor(), place, false);
|
||||
@@ -702,4 +721,33 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType inferQualifierTypeByPlace(GrReferenceExpression place) {
|
||||
if (place.getParent() instanceof GrIndexProperty) {
|
||||
return place.getType();
|
||||
}
|
||||
final GrExpression rtQualifier = PsiImplUtil.getRuntimeQualifier(place);
|
||||
if (rtQualifier != null) {
|
||||
return rtQualifier.getType();
|
||||
}
|
||||
|
||||
PsiClass containingClass = null;
|
||||
final GrMember member = PsiTreeUtil.getParentOfType(place, GrMember.class);
|
||||
if (member == null) {
|
||||
final PsiFile file = place.getContainingFile();
|
||||
assert file instanceof GroovyFile && ((GroovyFile)file).isScript();
|
||||
containingClass = ((GroovyFile)file).getScriptClass();
|
||||
}
|
||||
else if (member instanceof GrMethod) {
|
||||
if (!member.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
containingClass = member.getContainingClass();
|
||||
}
|
||||
}
|
||||
|
||||
if (containingClass != null) {
|
||||
return JavaPsiFacade.getElementFactory(place.getProject()).createType(containingClass);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.QueryExecutorBase;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.PsiSubstitutor;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.UsageSearchContext;
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch;
|
||||
@@ -45,7 +46,7 @@ public class AccessorMethodReferencesSearcher extends QueryExecutorBase<PsiRefer
|
||||
final PsiMethod method = queryParameters.getMethod();
|
||||
|
||||
final String propertyName;
|
||||
if (MixinMemberContributor.isCategoryMethod(method, null)) {
|
||||
if (MixinMemberContributor.isCategoryMethod(method, null, PsiSubstitutor.EMPTY)) {
|
||||
final GrGdkMethod cat = GrGdkMethodImpl.createGdkMethod(method, false);
|
||||
propertyName = GroovyPropertyUtils.getPropertyName((PsiMethod)cat);
|
||||
}
|
||||
|
||||
+8
-3
@@ -70,7 +70,7 @@ public class MixinMemberContributor extends NonCodeMembersContributor {
|
||||
if (!mixin.processDeclarations(new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(PsiElement element, ResolveState state) {
|
||||
if (isCategoryMethod(element, qualifierType)) {
|
||||
if (isCategoryMethod(element, qualifierType, state.get(PsiSubstitutor.KEY))) {
|
||||
return super.execute(GrGdkMethodImpl.createGdkMethod((PsiMethod)element, false), state);
|
||||
}
|
||||
else {
|
||||
@@ -93,7 +93,7 @@ public class MixinMemberContributor extends NonCodeMembersContributor {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isCategoryMethod(@Nullable PsiElement element, @Nullable PsiType qualifierType) {
|
||||
public static boolean isCategoryMethod(@Nullable PsiElement element, @Nullable PsiType qualifierType, @Nullable PsiSubstitutor substitutor) {
|
||||
if (!(element instanceof PsiMethod)) return false;
|
||||
if (!((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
|
||||
@@ -102,7 +102,12 @@ public class MixinMemberContributor extends NonCodeMembersContributor {
|
||||
|
||||
if (qualifierType == null) return true;
|
||||
|
||||
final PsiType selfType = parameters[0].getType();
|
||||
PsiType selfType = parameters[0].getType();
|
||||
if (selfType instanceof PsiPrimitiveType) return false;
|
||||
|
||||
if (substitutor != null) {
|
||||
selfType = substitutor.substitute(selfType);
|
||||
}
|
||||
return TypesUtil.isAssignable(selfType, qualifierType, element.getManager(), element.getResolveScope());
|
||||
}
|
||||
|
||||
|
||||
@@ -984,4 +984,29 @@ catch (ClassNotFoundException | <warning descr="Exception 'java.lang.NullPointer
|
||||
''')
|
||||
}
|
||||
|
||||
void testCategoryWithPrimitiveType() {
|
||||
testHighlighting('''\
|
||||
class Cat {
|
||||
static foo(Integer x) {}
|
||||
}
|
||||
|
||||
use(Cat) {
|
||||
1.with {
|
||||
foo()
|
||||
}
|
||||
|
||||
(1 as int).foo()
|
||||
}
|
||||
|
||||
class Ca {
|
||||
static foo(int x) {}
|
||||
}
|
||||
|
||||
use(Ca) {
|
||||
1.<warning descr="Category method 'foo' cannot be applied to 'java.lang.Integer'">foo</warning>()
|
||||
(1 as int).<warning descr="Category method 'foo' cannot be applied to 'java.lang.Integer'">foo</warning>()
|
||||
}
|
||||
''', GroovyAssignabilityCheckInspection)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user