[java] skip plain cannot infer diamond error (IDEA-283409); add ambiguous constructor call (IDEA-272115)

this unifies error messages with method calls and allows more specific tooltips and additional fixes

GitOrigin-RevId: 5ab4340d822bba3d6563da12a16e4c41216a8627
This commit is contained in:
Anna Kozlova
2021-11-26 15:05:23 +01:00
committed by intellij-monorepo-bot
parent 10f7715893
commit e6165048e8
13 changed files with 185 additions and 35 deletions

View File

@@ -109,11 +109,16 @@ public final class GenericsHighlightUtil {
if (!(inferenceResult.failedToInfer() && expectedType instanceof PsiClassType && ((PsiClassType)expectedType).isRaw())) {
HighlightInfo highlightInfo = HighlightInfo
.newHighlightInfo(HighlightInfoType.ERROR).range(referenceParameterList).descriptionAndTooltip(errorMessage).create();
if (inferenceResult == PsiDiamondType.DiamondInferenceResult.ANONYMOUS_INNER_RESULT &&
!PsiUtil.isLanguageLevel9OrHigher(referenceParameterList)) {
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(LanguageLevel.JDK_1_9));
if (inferenceResult == PsiDiamondType.DiamondInferenceResult.ANONYMOUS_INNER_RESULT) {
if (!PsiUtil.isLanguageLevel9OrHigher(referenceParameterList)) {
QuickFixAction.registerQuickFixAction(highlightInfo,
QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(LanguageLevel.JDK_1_9));
}
return highlightInfo;
}
if (inferenceResult == PsiDiamondType.DiamondInferenceResult.EXPLICIT_CONSTRUCTOR_TYPE_ARGS) {
return highlightInfo;
}
return highlightInfo;
}
}
@@ -1166,7 +1171,7 @@ public final class GenericsHighlightUtil {
}
PsiClassType type = JavaPsiFacade.getElementFactory(holder.getProject()).createType(containingClass);
HighlightMethodUtil.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null, holder, javaSdkVersion);
HighlightMethodUtil.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null, holder, javaSdkVersion, enumConstant.getArgumentList());
}
static HighlightInfo checkEnumSuperConstructorCall(@NotNull PsiMethodCallExpression expr) {

View File

@@ -559,7 +559,7 @@ public final class HighlightMethodUtil {
return null;
}
static HighlightInfo createIncompatibleTypeHighlightInfo(@NotNull PsiCallExpression methodCall,
static HighlightInfo createIncompatibleTypeHighlightInfo(@NotNull PsiCall methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull MethodCandidateInfo resolveResult,
@NotNull PsiElement elementToHighlight) {
@@ -1657,16 +1657,33 @@ public final class HighlightMethodUtil {
}
PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
checkConstructorCall(typeResult, expression, type, classReference, holder, javaSdkVersion);
checkConstructorCall(typeResult, expression, type, classReference, holder, javaSdkVersion, expression.getArgumentList());
}
static void checkAmbiguousConstructorCall(PsiJavaCodeReferenceElement ref,
PsiElement resolved,
PsiElement parent,
HighlightInfoHolder holder,
JavaSdkVersion version) {
if (resolved instanceof PsiClass && parent instanceof PsiNewExpression && ((PsiClass)resolved).getConstructors().length > 0) {
PsiNewExpression newExpression = (PsiNewExpression)parent;
if (newExpression.resolveMethod() == null && !PsiTreeUtil.findChildrenOfType(newExpression.getArgumentList(), PsiFunctionalExpression.class).isEmpty()) {
PsiType type = newExpression.getType();
if (type != null) {
checkConstructorCall(((PsiClassType)type).resolveGenerics(), newExpression, type, newExpression.getClassReference(), holder, version, ref);
}
}
}
}
static void checkConstructorCall(@NotNull PsiClassType.ClassResolveResult typeResolveResult,
@NotNull PsiConstructorCall constructorCall,
@NotNull PsiType type,
@Nullable PsiJavaCodeReferenceElement classReference,
@NotNull HighlightInfoHolder holder,
@NotNull JavaSdkVersion javaSdkVersion) {
@NotNull JavaSdkVersion javaSdkVersion,
@Nullable PsiElement elementToHighlight) {
if (elementToHighlight == null) return;
PsiExpressionList list = constructorCall.getArgumentList();
if (list == null) return;
PsiClass aClass = typeResolveResult.getElement();
@@ -1754,10 +1771,12 @@ public final class HighlightMethodUtil {
String name = aClass.getName();
name += buildArgTypesList(list);
String description = JavaErrorBundle.message("cannot.resolve.constructor", name);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).navigationShift(+1).create();
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(elementToHighlight).descriptionAndTooltip(description).create();
if (info != null) {
WrapExpressionFix.registerWrapAction(results, list.getExpressions(), info, getFixRange(list));
registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, info);
TextRange fixRange = getFixRange(elementToHighlight);
WrapExpressionFix.registerWrapAction(results, list.getExpressions(), info, fixRange);
registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, info, fixRange);
holder.add(info);
}
}
@@ -1773,7 +1792,7 @@ public final class HighlightMethodUtil {
if (constructorCall instanceof PsiNewExpression) {
methodCandidates = resolveHelper.getReferencedMethodCandidates((PsiCallExpression)constructorCall, true);
}
registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, methodCandidates, info);
registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, methodCandidates, info, getFixRange(list));
registerMethodReturnFixAction(info, result, constructorCall);
holder.add(info);
}
@@ -1789,6 +1808,9 @@ public final class HighlightMethodUtil {
if (result != null && !holder.hasErrorResults()) {
holder.add(checkVarargParameterErasureToBeAccessible(result, constructorCall));
}
if (result != null && !holder.hasErrorResults()) {
holder.add(createIncompatibleTypeHighlightInfo(constructorCall, resolveHelper, result, constructorCall));
}
}
}
@@ -1822,8 +1844,8 @@ public final class HighlightMethodUtil {
@NotNull PsiClass aClass,
PsiMethod @NotNull [] constructors,
JavaResolveResult @NotNull [] results,
@NotNull final HighlightInfo info) {
TextRange fixRange = getFixRange(list);
@NotNull final HighlightInfo info,
TextRange fixRange) {
if (classReference != null) {
ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info, fixRange);
ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass, fixRange);

View File

@@ -1221,6 +1221,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!myHolder.hasErrorResults() && resolved instanceof PsiModifierListOwner) {
ref.accept(myPreviewFeatureVisitor);
}
if (!myHolder.hasErrorResults()) {
HighlightMethodUtil.checkAmbiguousConstructorCall(ref, resolved, ref.getParent(), myHolder, myJavaSdkVersion);
}
}
}

View File

@@ -4,6 +4,6 @@ import java.util.List;
class Test {
{
List<List<String>> l = new ArrayList<List<error descr="Cannot infer arguments"><></error>>();
<error descr="Incompatible types. Found: 'java.util.ArrayList<java.util.List>', required: 'java.util.List<java.util.List<java.lang.String>>'">List<List<String>> l = new ArrayList<List<>>();</error>
}
}

View File

@@ -1,6 +1,6 @@
class Test {
{
D<String> ds = new D<error descr="Cannot infer arguments (unable to resolve constructor)"><></error>(9);
D<String> ds = new D<><error descr="Cannot resolve constructor 'D(int)'">(9)</error>;
}
}

View File

@@ -1,14 +1,14 @@
class Test {
{
Holder h = null;
Result<String> r1 = new Result<error descr="Cannot infer arguments"><></error>(h);
Result<String> r1 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">new Result<>(h);</error>
Result<String> r2 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">Result.create(h);</error>
Holder dataHolder = null;
Result<String> r3 = new Result<error descr="Cannot infer arguments"><></error>(new Holder<>(dataHolder));
Result<String> r3 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">new Result<>(new Holder<>(dataHolder));</error>
Result<String> r4 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">Result.create(new Holder<>(dataHolder));</error>
Result<String> r5 = new Result<error descr="Cannot infer arguments"><></error>(Holder.create(dataHolder));
Result<String> r5 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">new Result<>(Holder.create(dataHolder));</error>
Result<String> r6 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">Result.create(Holder.create(dataHolder));</error>
}

View File

@@ -0,0 +1,6 @@
class A<R>{
A(R value) {}
public static void main(String[] args) {
A<Integer> a = new A<><error descr="'A(R)' in 'A' cannot be applied to '(java.lang.String, int)'" tooltip="Expected 1 arguments but found 2">("hi", 1)</error>;
}
}

View File

@@ -29,7 +29,7 @@ public class ConcurrentCollectors {
static <T, K, D, M1 extends Map<K, D>> C<T, M1> groupingBy(F<M1> f,
C<T, D> c,
BiConsumer<M1, T> consumer) {
return new CImpl<error descr="Cannot infer arguments"><></error>(f, consumer, arg(c.getOp()));
return new CImpl<><error descr="'CImpl(ConcurrentCollectors.F<R>, ConcurrentCollectors.BiConsumer<R,T>, ConcurrentCollectors.BiOp<R>)' in 'ConcurrentCollectors.CImpl' cannot be applied to '(ConcurrentCollectors.F<M1>, ConcurrentCollectors.BiConsumer<M1,T>, ConcurrentCollectors.BiOp<ConcurrentCollectors.ConcurrentMap<java.lang.Object,D>>)'">(f, consumer, arg(c.getOp()))</error>;
}
static <K, V, M2 extends ConcurrentMap<K, V>> BiOp<M2> arg(BiOp<V> op) {

View File

@@ -0,0 +1,48 @@
import java.util.function.Function;
import java.util.function.Supplier;
class OverloadCast {
public void runMe() {
new <error descr="Cannot resolve constructor 'OverloadCast(<method reference>, <lambda expression>)'">OverloadCast</error>(WhitespaceTokenizer::<error descr="Cannot resolve constructor 'WhitespaceTokenizer'">new</error>, src -> new LowerCaseFilter<error descr="'LowerCaseFilter(OverloadCast.TokenStream)' in 'OverloadCast.LowerCaseFilter' cannot be applied to '(<lambda parameter>)'">(src)</error>);
<error descr="Ambiguous method call: both 'OverloadCast.overloadCast(Supplier<Tokenizer>, Function<TokenStream, TokenFilter>)' and 'OverloadCast.overloadCast(Function<TokenStream, TokenFilter>, Function<String, String>)' match">overloadCast</error>(WhitespaceTokenizer::<error descr="Cannot resolve constructor 'WhitespaceTokenizer'">new</error>, src -> new LowerCaseFilter<error descr="'LowerCaseFilter(OverloadCast.TokenStream)' in 'OverloadCast.LowerCaseFilter' cannot be applied to '(<lambda parameter>)'">(src)</error>);
}
private OverloadCast(Supplier<Tokenizer> tokenizerFactory, Function<TokenStream, TokenFilter> filterCreator) {
}
private OverloadCast(Function<TokenStream, TokenFilter> filterCreator, Function<String, String> readerWrapper) {
}
private void overloadCast(Supplier<Tokenizer> tokenizerFactory, Function<TokenStream, TokenFilter> filterCreator) {
}
private void overloadCast(Function<TokenStream, TokenFilter> filterCreator, Function<String, String> readerWrapper) {
}
private class Tokenizer {
}
private class TokenStream {
}
private class TokenFilter {
}
private class WhitespaceTokenizer extends Tokenizer {
private WhitespaceTokenizer(TokenStream s) {
}
private WhitespaceTokenizer() {
}
}
private class LowerCaseFilter extends TokenFilter {
public LowerCaseFilter(TokenStream src) {
super();
}
private LowerCaseFilter() {
}
}
}

View File

@@ -0,0 +1,39 @@
// "Cast 1st argument to 'Supplier<Tokenizer>'" "true"
import java.util.function.Function;
import java.util.function.Supplier;
class OverloadCast {
public void runMe() {
new OverloadCast((Supplier<Tokenizer>) WhitespaceTokenizer::new, src -> new LowerCaseFilter(src));
}
private OverloadCast(Supplier<Tokenizer> tokenizerFactory, Function<TokenStream, TokenFilter> filterCreator) {
}
private OverloadCast(Function<TokenStream, TokenFilter> filterCreator, Function<String, String> readerWrapper) {
}
private class Tokenizer { }
private class TokenStream { }
private class TokenFilter { }
private class WhitespaceTokenizer extends Tokenizer {
private WhitespaceTokenizer(TokenStream s) {
}
private WhitespaceTokenizer() {
}
}
private class LowerCaseFilter extends TokenFilter {
public LowerCaseFilter(TokenStream src) {
super();
}
private LowerCaseFilter() {
}
}
}

View File

@@ -0,0 +1,39 @@
// "Cast 1st argument to 'Supplier<Tokenizer>'" "true"
import java.util.function.Function;
import java.util.function.Supplier;
class OverloadCast {
public void runMe() {
new Over<caret>loadCast(WhitespaceTokenizer::new, src -> new LowerCaseFilter(src));
}
private OverloadCast(Supplier<Tokenizer> tokenizerFactory, Function<TokenStream, TokenFilter> filterCreator) {
}
private OverloadCast(Function<TokenStream, TokenFilter> filterCreator, Function<String, String> readerWrapper) {
}
private class Tokenizer { }
private class TokenStream { }
private class TokenFilter { }
private class WhitespaceTokenizer extends Tokenizer {
private WhitespaceTokenizer(TokenStream s) {
}
private WhitespaceTokenizer() {
}
}
private class LowerCaseFilter extends TokenFilter {
public LowerCaseFilter(TokenStream src) {
super();
}
private LowerCaseFilter() {
}
}
}

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 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.
*/
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon.lambda;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
@@ -114,6 +100,7 @@ public class Diamond8HighlightingTest extends LightDaemonAnalyzerTestCase {
public void testNestedDiamondsAnonymousCase() { doTest();}
public void testDiamondsUncheckedWarning() { doTest();}
public void testWrongNumberOfArguments() { doTest();}
private void doTest() {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);

View File

@@ -148,6 +148,7 @@ class NewLambdaHighlightingTest extends LightJavaCodeInsightFixtureTestCase5 {
@Test void testPolyExpressionInVoidCompatibleLambdaReturn() { doTest(); }
@Test void testStopAtTypeCastWhenSearchForTopMostNode() { doTest(); }
@Test void testLambdaWithExplicitTypeAndTargetTypeParameter() { doTest(); }
@Test void testAmbiguousConstructorCallWithLambdaInside() { doTest(); }
@Override
protected @NotNull String getRelativePath() {