KT-1138 Need better diagnostic for case when method/constructor call is followed by class initializer or simple block

This commit is contained in:
Andrey Breslav
2012-02-22 11:51:19 +04:00
parent 62ddef4734
commit b46e20c5c3
7 changed files with 195 additions and 3 deletions
@@ -488,6 +488,10 @@ public interface Errors {
ParameterizedDiagnosticFactory1<String> UNRESOLVED_IDE_TEMPLATE = new ParameterizedDiagnosticFactory1<String>(ERROR, "Unresolved IDE template: {0}");
SimpleDiagnosticFactory DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED = SimpleDiagnosticFactory.create(WARNING, "This expression is treated as an argument to the function call on the previous line. " +
"Separate it with a semicolon (;) if it is not intended to be an argument.");
// This field is needed to make the Initializer class load (interfaces cannot have static initializers)
@SuppressWarnings("UnusedDeclaration")
Initializer __initializer = Initializer.INSTANCE;
@@ -386,13 +386,20 @@ public class CallResolver {
assert callElement != null;
trace.report(UNNECESSARY_SAFE_CALL.on((JetElement) callOperationNode.getTreeParent().getPsi(), callOperationNode, type));
}
@Override
public void danglingFunctionLiteralArgumentSuspected(@NotNull BindingTrace trace, @NotNull List<JetExpression> functionLiteralArguments) {
for (JetExpression functionLiteralArgument : functionLiteralArguments) {
trace.report(DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED.on(functionLiteralArgument));
}
}
};
TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null;
OverloadResolutionResultsImpl<D> resultsForFirstNonemptyCandidateSet = null;
for (ResolutionTask<D> task : prioritizedTasks) {
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace);
OverloadResolutionResultsImpl<D> results = performResolution(temporaryTrace, scope, expectedType, task, tracing);
OverloadResolutionResultsImpl<D> results = performResolutionGuardedForExtraFunctionLiteralArguments(temporaryTrace, scope, expectedType, task, tracing);
if (results.isSuccess()) {
temporaryTrace.commit();
@@ -421,6 +428,53 @@ public class CallResolver {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@NotNull
private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> performResolutionGuardedForExtraFunctionLiteralArguments(
@NotNull BindingTrace trace,
@NotNull JetScope scope, @NotNull JetType expectedType,
@NotNull ResolutionTask<D> task, @NotNull TracingStrategy tracing
) {
OverloadResolutionResultsImpl<D> results = performResolution(trace, scope, expectedType, task, tracing);
// If resolution fails, we should check for some of the following situations:
// class A {
// val foo = Bar() // The following is intended to be an anonymous initializer,
// // but is treated as a function literal argument
// {
// ...
// }
// }
//
// fun foo() {
// bar {
// buzz()
// {...} // intended to be a returned from the outer literal
// }
// }
EnumSet<OverloadResolutionResults.Code> someFailed = EnumSet.of(OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES, OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
if (someFailed.contains(results.getResultCode()) && !task.getCall().getFunctionLiteralArguments().isEmpty()) {
// We have some candidates that failed for some reason
// And we have a suspect: the function literal argument
// Now, we try to remove this argument and see if it helps
Collection<ResolvedCallImpl<D>> newCandidates = Lists.newArrayList();
for (ResolvedCallImpl<D> candidate : task.getCandidates()) {
newCandidates.add(ResolvedCallImpl.create(candidate.getCandidateDescriptor()));
}
ResolutionTask<D> newTask = new ResolutionTask<D>(newCandidates, new DelegatingCall(task.getCall()) {
@NotNull
@Override
public List<JetExpression> getFunctionLiteralArguments() {
return Collections.emptyList();
}
}, task.getDataFlowInfo());
OverloadResolutionResultsImpl<D> resultsWithFunctionLiteralsStripped = performResolution(TemporaryBindingTrace.create(trace), scope, expectedType, newTask, tracing);
if (resultsWithFunctionLiteralsStripped.isSuccess() || resultsWithFunctionLiteralsStripped.isAmbiguity()) {
tracing.danglingFunctionLiteralArgumentSuspected(trace, task.getCall().getFunctionLiteralArguments());
}
}
return results;
}
@NotNull
private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> performResolution(
@NotNull BindingTrace trace,
@@ -0,0 +1,98 @@
/*
* 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.jet.lang.resolve.calls;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import java.util.List;
/**
* @author abreslav
*/
public class DelegatingCall implements Call {
private final Call delegate;
public DelegatingCall(@NotNull Call delegate) {
this.delegate = delegate;
}
@Override
@Nullable
public ASTNode getCallOperationNode() {
return delegate.getCallOperationNode();
}
@Override
@NotNull
public ReceiverDescriptor getExplicitReceiver() {
return delegate.getExplicitReceiver();
}
@Override
@Nullable
public JetExpression getCalleeExpression() {
return delegate.getCalleeExpression();
}
@Override
@Nullable
public JetValueArgumentList getValueArgumentList() {
return delegate.getValueArgumentList();
}
@Override
@NotNull
public List<? extends ValueArgument> getValueArguments() {
return delegate.getValueArguments();
}
@Override
@NotNull
public List<JetExpression> getFunctionLiteralArguments() {
return delegate.getFunctionLiteralArguments();
}
@Override
@NotNull
public List<JetTypeProjection> getTypeArguments() {
return delegate.getTypeArguments();
}
@Override
@Nullable
public JetTypeArgumentList getTypeArgumentList() {
return delegate.getTypeArgumentList();
}
@Override
@NotNull
public ASTNode getCallNode() {
return delegate.getCallNode();
}
@Override
@Nullable
public PsiElement getCallElement() {
return delegate.getCallElement();
}
}
@@ -19,12 +19,14 @@ package org.jetbrains.jet.lang.resolve.calls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.inference.SolutionStatus;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import java.util.Collection;
import java.util.List;
/**
* @author abreslav
@@ -72,6 +74,9 @@ import java.util.Collection;
@Override
public void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type) {}
@Override
public void danglingFunctionLiteralArgumentSuspected(@NotNull BindingTrace trace, @NotNull List<JetExpression> functionLiteralArguments) {}
};
<D extends CallableDescriptor> void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallImpl<D> resolvedCall);
@@ -101,4 +106,6 @@ import java.util.Collection;
void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type);
void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type);
void danglingFunctionLiteralArgumentSuspected(@NotNull BindingTrace trace, @NotNull List<JetExpression> functionLiteralArguments);
}
@@ -0,0 +1,29 @@
//+JDK
class Foo() {
private val builder = StringBuilder("sdfsd");
{
}
}
class Foo1() {
private val builder = <!NONE_APPLICABLE!>StringBuilder<!>("sdfsd")
<!DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED!>{
}<!>
}
fun foo() = {
<!NONE_APPLICABLE!>println<!>(1)
<!DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED!>{}<!>
}
fun foo1() = {
println(1);
{}
}
fun println(<!UNUSED_PARAMETER!>i<!> : Int) {}
fun println(<!UNUSED_PARAMETER!>s<!> : Byte) {}
fun println() {}
@@ -32,7 +32,7 @@ fun main(args : Array<String>) {
<!UNRESOLVED_REFERENCE!>a<!>.foo1()(<!UNRESOLVED_REFERENCE!>a<!>)
foo2()({})
foo2()<!TOO_MANY_ARGUMENTS!>{}<!>
foo2()<!TOO_MANY_ARGUMENTS, DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED!>{}<!>
(foo2()){}
(foo2())<!TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE!>x<!> -> }<!>
foo2()(<!TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE!>x<!> -> }<!>)
@@ -13,5 +13,5 @@ fun test() {
v1({}, <!ERROR_COMPILE_TIME_VALUE!>1<!>, {})
v1({}, {}, {it})
v1({}) <!VARARG_OUTSIDE_PARENTHESES!>{}<!>
v1 <!VARARG_OUTSIDE_PARENTHESES!>{}<!>
v1 <!VARARG_OUTSIDE_PARENTHESES, DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED!>{}<!>
}