diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties index 57e634e0a377..dc9815c58b32 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties @@ -265,6 +265,7 @@ invoke.completion.second.time.to.show.skipped.methods=Invoke completion second t groovy.file.extension.is.not.mapped.to.groovy.file.type=*.groovy files are mapped to ''{0}''.\nYou can map them to Groovy in Settings | File types class.is.not.annotation=''{0}'' is not an annotation method.call.is.ambiguous=Method call is ambiguous +operator.call.is.ambiguous=Operator call is ambiguous duplicated.named.parameter=Duplicated named parameter ''{0}'' found no.super.classes.found=No super classes found no.super.method.found=No super methods found diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/GroovyTypeCheckVisitor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/GroovyTypeCheckVisitor.java index 196541689c9a..432012adca3e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/GroovyTypeCheckVisitor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/GroovyTypeCheckVisitor.java @@ -15,6 +15,10 @@ import org.jetbrains.plugins.groovy.annotator.GrHighlightUtil; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; import org.jetbrains.plugins.groovy.codeInspection.assignment.*; +import org.jetbrains.plugins.groovy.codeInspection.type.highlighting.BinaryExpressionHighlighter; +import org.jetbrains.plugins.groovy.codeInspection.type.highlighting.GrConstructorInvocationHighlighter; +import org.jetbrains.plugins.groovy.codeInspection.type.highlighting.GrEnumConstantHighlighter; +import org.jetbrains.plugins.groovy.codeInspection.type.highlighting.GrNewExpressionHighlighter; import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; import org.jetbrains.plugins.groovy.extensions.GroovyNamedArgumentProvider; import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor; @@ -64,7 +68,6 @@ import java.util.Objects; import static com.intellij.psi.util.PsiUtil.extractIterableTypeParameter; import static org.jetbrains.plugins.groovy.codeInspection.type.GroovyTypeCheckVisitorHelper.*; -import static org.jetbrains.plugins.groovy.codeInspection.type.ImplKt.processConstructor; import static org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils.isImplicitReturnStatement; import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyExpressionUtil.isFake; @@ -389,7 +392,10 @@ public class GroovyTypeCheckVisitor extends BaseInspectionVisitor { } private void checkNamedArgumentsType(@NotNull CallInfo info) { - GroovyPsiElement rawCall = info.getCall(); + checkNamedArgumentsType(info.getCall()); + } + + private void checkNamedArgumentsType(@NotNull GroovyPsiElement rawCall) { if (!(rawCall instanceof GrCall)) return; GrCall call = (GrCall)rawCall; @@ -432,32 +438,6 @@ public class GroovyTypeCheckVisitor extends BaseInspectionVisitor { } } - private void checkOperator(@NotNull CallInfo info) { - if (hasErrorElements(info.getCall())) return; - - GroovyResolveResult[] results = info.multiResolve(); - GroovyResolveResult resolveResult = info.advancedResolve(); - - if (isOperatorWithSimpleTypes(info.getCall(), resolveResult)) return; - - if (!checkCannotInferArgumentTypes(info)) return; - - if (resolveResult.getElement() != null) { - checkMethodApplicability(resolveResult, true, info); - } - else if (results.length > 0) { - for (GroovyResolveResult result : results) { - if (!checkMethodApplicability(result, false, info)) return; - } - - registerError( - info.getElementToHighlight(), - ProblemHighlightType.GENERIC_ERROR, - GroovyBundle.message("method.call.is.ambiguous") - ); - } - } - private void highlightInapplicableMethodUsage(@NotNull GroovyResolveResult methodResolveResult, @NotNull CallInfo info, @NotNull PsiMethod method) { @@ -719,13 +699,11 @@ public class GroovyTypeCheckVisitor extends BaseInspectionVisitor { final GroovyCallReference reference = newExpression.getConstructorReference(); if (reference == null) return; - - final GrNewExpressionInfo info = new GrNewExpressionInfo(newExpression); - if (processConstructor(reference, newExpression.getArgumentList(), info.getElementToHighlight(), myHighlightSink)) { + if (new GrNewExpressionHighlighter(newExpression, reference, myHighlightSink).highlight()) { return; } - checkNamedArgumentsType(info); + checkNamedArgumentsType(newExpression); } @Override @@ -733,13 +711,11 @@ public class GroovyTypeCheckVisitor extends BaseInspectionVisitor { super.visitEnumConstant(enumConstant); if (hasErrorElements(enumConstant) || hasErrorElements(enumConstant.getArgumentList())) return; - final GrEnumConstantInfo info = new GrEnumConstantInfo(enumConstant); - final GroovyCallReference reference = enumConstant.getConstructorReference(); - if (processConstructor(reference, enumConstant.getArgumentList(), info.getElementToHighlight(), myHighlightSink)) { + if (new GrEnumConstantHighlighter(enumConstant, myHighlightSink).highlight()) { return; } - checkNamedArgumentsType(info); + checkNamedArgumentsType(enumConstant); } @Override @@ -747,12 +723,10 @@ public class GroovyTypeCheckVisitor extends BaseInspectionVisitor { super.visitConstructorInvocation(invocation); if (hasErrorElements(invocation) || hasErrorElements(invocation.getArgumentList())) return; - final GrConstructorInvocationInfo info = new GrConstructorInvocationInfo(invocation); - final GroovyCallReference reference = invocation.getConstructorReference(); - if (processConstructor(reference, invocation.getArgumentList(), info.getElementToHighlight(), myHighlightSink)) { + if (new GrConstructorInvocationHighlighter(invocation, myHighlightSink).highlight()) { return; } - checkNamedArgumentsType(info); + checkNamedArgumentsType(invocation); } @Override @@ -794,8 +768,9 @@ public class GroovyTypeCheckVisitor extends BaseInspectionVisitor { @Override public void visitBinaryExpression(@NotNull GrBinaryExpression binary) { super.visitBinaryExpression(binary); - if (isFake(binary)) return; - checkOperator(new GrBinaryExprInfo(binary)); + GroovyCallReference reference = binary.getReference(); + if (reference == null) return; + new BinaryExpressionHighlighter(binary, reference, myHighlightSink).highlight(); } @Override diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting.kt deleted file mode 100644 index b2377f7849aa..000000000000 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting.kt +++ /dev/null @@ -1,74 +0,0 @@ -// 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.codeInspection.type - -import com.intellij.codeInspection.LocalQuickFix -import com.intellij.codeInspection.ProblemHighlightType -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiType -import com.intellij.util.containers.toArray -import org.jetbrains.plugins.groovy.GroovyBundle.message -import org.jetbrains.plugins.groovy.highlighting.HighlightSink -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod -import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.createSignature -import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.DefaultConstructor -import org.jetbrains.plugins.groovy.lang.resolve.api.Argument -import org.jetbrains.plugins.groovy.lang.resolve.api.Arguments - -fun HighlightSink.highlightUnknownArgs(highlightElement: PsiElement) { - registerProblem(highlightElement, ProblemHighlightType.WEAK_WARNING, message("cannot.infer.argument.types")) -} - -fun HighlightSink.highlightCannotApplyError(invokedText: String, typesString: String, highlightElement: PsiElement) { - registerError(highlightElement, message("cannot.apply.method.or.closure", invokedText, typesString)) -} - -fun HighlightSink.highlightAmbiguousMethod(highlightElement: PsiElement) { - registerError(highlightElement, message("constructor.call.is.ambiguous")) -} - -fun HighlightSink.highlightInapplicableMethod(result: GroovyMethodResult, - arguments: List, - argumentList: GrArgumentList?, - highlightElement: PsiElement) { - val method = result.element - val containingClass = if (method is GrGdkMethod) method.staticMethod.containingClass else method.containingClass - - val argumentString = argumentsString(arguments) - val methodName = method.name - if (containingClass == null) { - highlightCannotApplyError(methodName, argumentString, highlightElement) - return - } - - val message: String - if (method is DefaultConstructor) { - message = message("cannot.apply.default.constructor", methodName) - } - else { - val factory = JavaPsiFacade.getElementFactory(method.project) - val containingType = factory.createType(containingClass, result.substitutor) - val canonicalText = containingType.internalCanonicalText - if (method.isConstructor) { - message = message("cannot.apply.constructor", methodName, canonicalText, argumentString) - } - else { - message = message("cannot.apply.method1", methodName, canonicalText, argumentString) - } - } - val fixes = generateCastFixes(result, arguments, argumentList) - registerProblem(highlightElement, ProblemHighlightType.GENERIC_ERROR, message, *fixes) -} - -private fun argumentsString(arguments: List): String { - return arguments.joinToString(", ", "(", ")") { - it.type?.internalCanonicalText ?: "?" - } -} - -private fun generateCastFixes(result: GroovyMethodResult, arguments: Arguments, argumentList: GrArgumentList?): Array { - val signature = createSignature(result.element, result.substitutor) - return GroovyTypeCheckVisitorHelper.genCastFixes(signature, arguments.map(Argument::type).toArray(PsiType.EMPTY_ARRAY), argumentList) -} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/BinaryExpressionHighlighter.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/BinaryExpressionHighlighter.kt new file mode 100644 index 000000000000..1e1d0f01edad --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/BinaryExpressionHighlighter.kt @@ -0,0 +1,54 @@ +// 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.codeInspection.type.highlighting + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import org.jetbrains.plugins.groovy.GroovyBundle +import org.jetbrains.plugins.groovy.codeInspection.assignment.GrCastFix +import org.jetbrains.plugins.groovy.codeInspection.type.GroovyTypeCheckVisitorHelper.hasErrorElements +import org.jetbrains.plugins.groovy.codeInspection.type.GroovyTypeCheckVisitorHelper.isOperatorWithSimpleTypes +import org.jetbrains.plugins.groovy.highlighting.HighlightSink +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression +import org.jetbrains.plugins.groovy.lang.psi.util.isFake +import org.jetbrains.plugins.groovy.lang.resolve.api.* + +class BinaryExpressionHighlighter(val expression: GrBinaryExpression, + reference: GroovyCallReference, + sink: HighlightSink) : CallReferenceHighlighter(reference, sink) { + + override val unknownArgsMessage: String + get() = GroovyBundle.message("cannot.infer.argument.types") + override val ambiguousMethodMessage: String + get() = GroovyBundle.message("operator.call.is.ambiguous") + + override fun getInapplicableMethodMessage(result: GroovyMethodResult, containingType: PsiType, arguments: Arguments): String { + val typeText = containingType.internalCanonicalText + val argumentsString = argumentsString(arguments) + return GroovyBundle.message("cannot.apply.method1", result.element.name, typeText, argumentsString) + } + + override fun getHighlightElement(): PsiElement = expression.operationToken + + override fun buildFix(argument: Argument, applicabilityData: ApplicabilityData): GrCastFix? { + if (argument !is ExpressionArgument || applicabilityData.applicability != Applicability.inapplicable) return null + val arguments = reference.arguments ?: return null + if (argument !in arguments) return null + val type = applicabilityData.type ?: return null + + val name = "Cast operand to " + type.presentableText + return GrCastFix(type, argument.expression, true, name) + } + + fun highlight() { + if (expression.isFake()) return + if (hasErrorElements(expression)) return + + val reference = expression.reference ?: return + val resolveResult = reference.advancedResolve() + + if (isOperatorWithSimpleTypes(expression, resolveResult)) return + + highlightMethod() + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/CallReferenceHighlighter.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/CallReferenceHighlighter.kt new file mode 100644 index 000000000000..6e8620274e63 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/CallReferenceHighlighter.kt @@ -0,0 +1,105 @@ +// 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.codeInspection.type.highlighting + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import org.jetbrains.plugins.groovy.GroovyBundle +import org.jetbrains.plugins.groovy.highlighting.HighlightSink +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod +import org.jetbrains.plugins.groovy.lang.resolve.api.* + +abstract class CallReferenceHighlighter(val reference: GroovyCallReference, val sink: HighlightSink) { + + abstract val unknownArgsMessage: String + + abstract val ambiguousMethodMessage: String + + abstract fun getInapplicableMethodMessage(result: GroovyMethodResult, containingType: PsiType, arguments: Arguments): String + + abstract fun getHighlightElement(): PsiElement + + abstract fun buildFix(argument: Argument, applicabilityData: ApplicabilityData): LocalQuickFix? + + fun highlightCannotApplyError(invokedText: String, typesString: String) { + sink.registerError(getHighlightElement(), GroovyBundle.message("cannot.apply.method.or.closure", invokedText, typesString)) + } + + fun highlightUnknownArgs() { + sink.registerProblem(getHighlightElement(), ProblemHighlightType.WEAK_WARNING, unknownArgsMessage) + } + + fun highlightAmbiguousMethod() { + sink.registerError(getHighlightElement(), ambiguousMethodMessage) + } + + fun highlightInapplicableMethod(result: GroovyMethodResult, arguments: Arguments) { + val method = result.element + val containingClass = if (method is GrGdkMethod) method.staticMethod.containingClass else method.containingClass + + val methodName = method.name + val highlightElement = getHighlightElement() + val argumentsString = argumentsString(arguments) + if (containingClass == null) { + highlightCannotApplyError(methodName, argumentsString) + return + } + val factory = JavaPsiFacade.getElementFactory(method.project) + val containingType = factory.createType(containingClass, result.substitutor) + + val fixes = generateFixes(result) + val message = getInapplicableMethodMessage(result, containingType, arguments) + sink.registerProblem(highlightElement, ProblemHighlightType.GENERIC_ERROR, message, *fixes) + } + + fun highlightMethod(): Boolean { + val userArguments = reference.arguments ?: run { + highlightUnknownArgs() + return true + } + + val results = reference.resolve(false) + var hasUnknownResults = false + for (result in results) { + if (result !is GroovyMethodResult) continue + val candidate = result.candidate ?: continue + val mapping = candidate.argumentMapping + if (mapping == null) { + highlightInapplicableMethod(result, userArguments) + return true + } + val applicability = mapping.applicability(result.substitutor, false) + when (applicability) { + Applicability.inapplicable -> { + highlightInapplicableMethod(result, userArguments) + return true + } + Applicability.canBeApplicable -> hasUnknownResults = true + else -> Unit + } + } + + if (results.size > 1) { + if (hasUnknownResults) { + highlightUnknownArgs() + return true + } + else { + highlightAmbiguousMethod() + return true + } + } + return false + } + + fun generateFixes(result: GroovyMethodResult): Array { + val applicabilities = result.candidate?.argumentMapping?.highlightApplicabilities(result.substitutor) + ?: return emptyArray() + return applicabilities.entries + .mapNotNull { (argument, applicabilities) -> buildFix(argument, applicabilities) } + .toTypedArray() + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/ConstructorCallHighlighter.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/ConstructorCallHighlighter.kt new file mode 100644 index 000000000000..91af6f6a7cb9 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/ConstructorCallHighlighter.kt @@ -0,0 +1,48 @@ +// 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.codeInspection.type.highlighting + +import com.intellij.psi.PsiType +import org.jetbrains.plugins.groovy.GroovyBundle +import org.jetbrains.plugins.groovy.codeInspection.assignment.ParameterCastFix +import org.jetbrains.plugins.groovy.highlighting.HighlightSink +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.DefaultConstructor +import org.jetbrains.plugins.groovy.lang.resolve.api.* + +abstract class ConstructorCallHighlighter(reference: GroovyCallReference, + sink: HighlightSink) : CallReferenceHighlighter(reference, sink) { + + abstract fun getArgumentList(): GrArgumentList? + + override val unknownArgsMessage: String + get() = GroovyBundle.message("cannot.infer.argument.types") + override val ambiguousMethodMessage: String + get() = GroovyBundle.message("method.call.is.ambiguous") + + override fun getInapplicableMethodMessage(result: GroovyMethodResult, containingType: PsiType, arguments: Arguments): String { + val method = result.element + val methodName = method.name + if (method is DefaultConstructor) { + return GroovyBundle.message("cannot.apply.default.constructor", methodName) + } + else { + val canonicalText = containingType.internalCanonicalText + return GroovyBundle.message("cannot.apply.constructor", methodName, canonicalText, + argumentsString(arguments)) + } + } + + override fun buildFix(argument: Argument, applicabilityData: ApplicabilityData): ParameterCastFix? { + if (argument !is ExpressionArgument || applicabilityData.applicability != Applicability.inapplicable) return null + val arguments = reference.arguments ?: return null + val list = getArgumentList() ?: return null + if (argument !in arguments) return null + val type = applicabilityData.type ?: return null + + val position = list.getExpressionArgumentIndex(argument.expression) + return ParameterCastFix(position, type) + } + + fun highlight(): Boolean = highlightMethod() +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrConstructorInvocationHighlighter.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrConstructorInvocationHighlighter.kt new file mode 100644 index 000000000000..b8cc244f947f --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrConstructorInvocationHighlighter.kt @@ -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.codeInspection.type.highlighting + +import com.intellij.psi.PsiElement +import org.jetbrains.plugins.groovy.highlighting.HighlightSink +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList + +class GrConstructorInvocationHighlighter(val invocation: GrConstructorInvocation, + sink: HighlightSink) : ConstructorCallHighlighter(invocation.constructorReference, sink) { + + override fun getArgumentList(): GrArgumentList = invocation.argumentList + + override fun getHighlightElement(): PsiElement = getArgumentList() +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrEnumConstantHighlighter.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrEnumConstantHighlighter.kt new file mode 100644 index 000000000000..56c6ebd7ea56 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrEnumConstantHighlighter.kt @@ -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.codeInspection.type.highlighting + +import com.intellij.psi.PsiElement +import org.jetbrains.plugins.groovy.highlighting.HighlightSink +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant + +class GrEnumConstantHighlighter(val enumConstant: GrEnumConstant, + sink: HighlightSink) : ConstructorCallHighlighter(enumConstant.constructorReference, sink) { + + override fun getArgumentList(): GrArgumentList? = enumConstant.argumentList + + override fun getHighlightElement(): PsiElement = getArgumentList() ?: enumConstant.nameIdentifierGroovy +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrNewExpressionHighlighter.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrNewExpressionHighlighter.kt new file mode 100644 index 000000000000..82acc4accf0c --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/GrNewExpressionHighlighter.kt @@ -0,0 +1,22 @@ +// 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.codeInspection.type.highlighting + +import com.intellij.psi.PsiElement +import com.intellij.util.IncorrectOperationException +import org.jetbrains.plugins.groovy.highlighting.HighlightSink +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression +import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyCallReference + +class GrNewExpressionHighlighter(val newExpression: GrNewExpression, + reference: GroovyCallReference, + sink: HighlightSink) : ConstructorCallHighlighter(reference, sink) { + + override fun getArgumentList(): GrArgumentList? = newExpression.argumentList + + override fun getHighlightElement(): PsiElement { + val element = getArgumentList() ?: newExpression.referenceElement + if (element != null) return element + throw IncorrectOperationException("reference of new expression should exist if it is a constructor call") + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/highlighting.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/highlighting.kt new file mode 100644 index 000000000000..369d76674a31 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/highlighting/highlighting.kt @@ -0,0 +1,10 @@ +// 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.codeInspection.type.highlighting + +import org.jetbrains.plugins.groovy.lang.resolve.api.Arguments + +fun argumentsString(arguments: Arguments): String { + return arguments.joinToString(", ", "(", ")") { + it.type?.internalCanonicalText ?: "?" + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/impl.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/impl.kt deleted file mode 100644 index be88bdbf1684..000000000000 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/type/impl.kt +++ /dev/null @@ -1,52 +0,0 @@ -// 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.codeInspection.type - -import com.intellij.psi.PsiElement -import org.jetbrains.plugins.groovy.highlighting.HighlightSink -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList -import org.jetbrains.plugins.groovy.lang.resolve.api.Applicability -import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyCallReference - -fun processConstructor(reference: GroovyCallReference, - argumentList: GrArgumentList?, - highlightElement: PsiElement, - sink: HighlightSink): Boolean { - val userArguments = reference.arguments ?: run { - sink.highlightUnknownArgs(highlightElement) - return true - } - - val results = reference.resolve(false) - var hasUnknownResults = false - for (result in results) { - if (result !is GroovyMethodResult) continue - val candidate = result.candidate ?: continue - val mapping = candidate.argumentMapping - if (mapping == null) { - sink.highlightInapplicableMethod(result, userArguments, argumentList, highlightElement) - return true - } - val applicability = mapping.applicability(result.substitutor, false) - when (applicability) { - Applicability.inapplicable -> { - sink.highlightInapplicableMethod(result, mapping.arguments, argumentList, highlightElement) - return true - } - Applicability.canBeApplicable -> hasUnknownResults = true - else -> Unit - } - } - - if (results.size > 1) { - if (hasUnknownResults) { - sink.highlightUnknownArgs(highlightElement) - return true - } - else { - sink.highlightAmbiguousMethod(highlightElement) - return true - } - } - return false -} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/api/api.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/api/api.kt index 64d8948bda26..2477d3bdf409 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/api/api.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/api/api.kt @@ -7,4 +7,4 @@ typealias Arguments = List typealias Applicabilities = Map -data class ApplicabilityData(val psiType: PsiType?, val applicability: Applicability) \ No newline at end of file +data class ApplicabilityData(val type: PsiType?, val applicability: Applicability) \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/inspections/CastToTypeTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/inspections/CastToTypeTest.groovy index 9d24c9b09926..270643b54783 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/inspections/CastToTypeTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/inspections/CastToTypeTest.groovy @@ -17,14 +17,19 @@ package org.jetbrains.plugins.groovy.inspections import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.LightGroovyTestCase import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection import org.jetbrains.plugins.groovy.util.TestUtils /** * @author Maxim.Medvedev */ -class CastToTypeTest extends LightCodeInsightFixtureTestCase { +class CastToTypeTest extends LightGroovyTestCase { + + final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST + @Override protected String getBasePath() { return TestUtils.testDataPath + 'groovy/inspections/castToType' @@ -42,4 +47,6 @@ class CastToTypeTest extends LightCodeInsightFixtureTestCase { void testSimple() {doTest('Cast to List')} void testInReturnType() {doTest('Cast to int')} void testInForCycle() {doTest('Cast to List')} + + void testInBinaryExpression() {doTest('Cast operand to String')} } diff --git a/plugins/groovy/testdata/groovy/inspections/castToType/inBinaryExpression.groovy b/plugins/groovy/testdata/groovy/inspections/castToType/inBinaryExpression.groovy new file mode 100644 index 000000000000..b8bf355c8dc2 --- /dev/null +++ b/plugins/groovy/testdata/groovy/inspections/castToType/inBinaryExpression.groovy @@ -0,0 +1,11 @@ +import groovy.transform.CompileStatic + +class C { + def plus(String s) { + + } +} +@CompileStatic +def foo() { + new C() + 1 +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/inspections/castToType/inBinaryExpression_after.groovy b/plugins/groovy/testdata/groovy/inspections/castToType/inBinaryExpression_after.groovy new file mode 100644 index 000000000000..ecc62472ed5d --- /dev/null +++ b/plugins/groovy/testdata/groovy/inspections/castToType/inBinaryExpression_after.groovy @@ -0,0 +1,11 @@ +import groovy.transform.CompileStatic + +class C { + def plus(String s) { + + } +} +@CompileStatic +def foo() { + new C() + (1 as String) +} \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/InnerClassConstructorNoArg.groovy b/plugins/groovy/testdata/highlighting/InnerClassConstructorNoArg.groovy index b548a56167e8..a6039c4a9ff1 100644 --- a/plugins/groovy/testdata/highlighting/InnerClassConstructorNoArg.groovy +++ b/plugins/groovy/testdata/highlighting/InnerClassConstructorNoArg.groovy @@ -3,13 +3,13 @@ class A { Inner() {} def foo() { new Inner() // correct - new Inner(new A()) // incorrect + new Inner(new A()) // incorrect } } def foo() { new Inner() //correct - new Inner(new A()) // incorrect + new Inner(new A()) // incorrect } static def bar() { diff --git a/plugins/groovy/testdata/highlighting/InnerClassConstructorWithAnotherArg.groovy b/plugins/groovy/testdata/highlighting/InnerClassConstructorWithAnotherArg.groovy index dd1dab93fbaf..a9a1a1aee0ee 100644 --- a/plugins/groovy/testdata/highlighting/InnerClassConstructorWithAnotherArg.groovy +++ b/plugins/groovy/testdata/highlighting/InnerClassConstructorWithAnotherArg.groovy @@ -4,10 +4,10 @@ class A { } def foo() { - new Inner() // incorrect - new Inner(new A()) // incorrect + new Inner() // incorrect + new Inner(new A()) // incorrect new Inner("") // correct - new Inner(new A(), "") // incorrect + new Inner(new A(), "") // incorrect } static def bar() { diff --git a/plugins/groovy/testdata/highlighting/InnerClassConstructorWithArg.groovy b/plugins/groovy/testdata/highlighting/InnerClassConstructorWithArg.groovy index 6864b1ed5bea..dbc1a394b9df 100644 --- a/plugins/groovy/testdata/highlighting/InnerClassConstructorWithArg.groovy +++ b/plugins/groovy/testdata/highlighting/InnerClassConstructorWithArg.groovy @@ -4,9 +4,9 @@ class A { } def foo() { - new Inner() // incorrect + new Inner() // incorrect new Inner(new A()) // correct - new Inner(new A(), new A()) // incorrect + new Inner(new A(), new A()) // incorrect } static def bar() {