From b46e20c5c3341b5a521b553008fe940348477d56 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 22 Feb 2012 11:51:19 +0400 Subject: [PATCH] KT-1138 Need better diagnostic for case when method/constructor call is followed by class initializer or simple block --- .../jet/lang/diagnostics/Errors.java | 4 + .../jet/lang/resolve/calls/CallResolver.java | 56 ++++++++++- .../lang/resolve/calls/DelegatingCall.java | 98 +++++++++++++++++++ .../lang/resolve/calls/TracingStrategy.java | 7 ++ .../tests/DanglingFunctionLiteral.jet | 29 ++++++ .../tests/FunctionCalleeExpressions.jet | 2 +- .../testData/diagnostics/tests/Varargs.jet | 2 +- 7 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DelegatingCall.java create mode 100644 compiler/testData/diagnostics/tests/DanglingFunctionLiteral.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 84ef814807b..ba572dde6fc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -488,6 +488,10 @@ public interface Errors { ParameterizedDiagnosticFactory1 UNRESOLVED_IDE_TEMPLATE = new ParameterizedDiagnosticFactory1(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; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 15d5f980845..5fcdfeceb96 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -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 functionLiteralArguments) { + for (JetExpression functionLiteralArgument : functionLiteralArguments) { + trace.report(DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED.on(functionLiteralArgument)); + } + } }; TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null; OverloadResolutionResultsImpl resultsForFirstNonemptyCandidateSet = null; for (ResolutionTask task : prioritizedTasks) { TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace); - OverloadResolutionResultsImpl results = performResolution(temporaryTrace, scope, expectedType, task, tracing); + OverloadResolutionResultsImpl results = performResolutionGuardedForExtraFunctionLiteralArguments(temporaryTrace, scope, expectedType, task, tracing); if (results.isSuccess()) { temporaryTrace.commit(); @@ -421,6 +428,53 @@ public class CallResolver { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @NotNull + private OverloadResolutionResultsImpl performResolutionGuardedForExtraFunctionLiteralArguments( + @NotNull BindingTrace trace, + @NotNull JetScope scope, @NotNull JetType expectedType, + @NotNull ResolutionTask task, @NotNull TracingStrategy tracing + ) { + OverloadResolutionResultsImpl 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 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> newCandidates = Lists.newArrayList(); + for (ResolvedCallImpl candidate : task.getCandidates()) { + newCandidates.add(ResolvedCallImpl.create(candidate.getCandidateDescriptor())); + } + ResolutionTask newTask = new ResolutionTask(newCandidates, new DelegatingCall(task.getCall()) { + @NotNull + @Override + public List getFunctionLiteralArguments() { + return Collections.emptyList(); + } + }, task.getDataFlowInfo()); + OverloadResolutionResultsImpl resultsWithFunctionLiteralsStripped = performResolution(TemporaryBindingTrace.create(trace), scope, expectedType, newTask, tracing); + if (resultsWithFunctionLiteralsStripped.isSuccess() || resultsWithFunctionLiteralsStripped.isAmbiguity()) { + tracing.danglingFunctionLiteralArgumentSuspected(trace, task.getCall().getFunctionLiteralArguments()); + } + } + + return results; + } + @NotNull private OverloadResolutionResultsImpl performResolution( @NotNull BindingTrace trace, diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DelegatingCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DelegatingCall.java new file mode 100644 index 00000000000..6e4b15c3cff --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DelegatingCall.java @@ -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 getValueArguments() { + return delegate.getValueArguments(); + } + + @Override + @NotNull + public List getFunctionLiteralArguments() { + return delegate.getFunctionLiteralArguments(); + } + + @Override + @NotNull + public List 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(); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java index 6ec722914d4..25e669b70a3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java @@ -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 functionLiteralArguments) {} }; void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallImpl 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 functionLiteralArguments); } diff --git a/compiler/testData/diagnostics/tests/DanglingFunctionLiteral.jet b/compiler/testData/diagnostics/tests/DanglingFunctionLiteral.jet new file mode 100644 index 00000000000..1b110ac6951 --- /dev/null +++ b/compiler/testData/diagnostics/tests/DanglingFunctionLiteral.jet @@ -0,0 +1,29 @@ +//+JDK + +class Foo() { + private val builder = StringBuilder("sdfsd"); + + { + } +} + +class Foo1() { + private val builder = StringBuilder("sdfsd") + + { + } +} + +fun foo() = { + println(1) + {} +} + +fun foo1() = { + println(1); + {} +} + +fun println(i : Int) {} +fun println(s : Byte) {} +fun println() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.jet b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.jet index b190d98be31..744c59cbd80 100644 --- a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.jet +++ b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.jet @@ -32,7 +32,7 @@ fun main(args : Array) { a.foo1()(a) foo2()({}) - foo2(){} + foo2(){} (foo2()){} (foo2()){x -> } foo2()({x -> }) diff --git a/compiler/testData/diagnostics/tests/Varargs.jet b/compiler/testData/diagnostics/tests/Varargs.jet index e9a33960df3..60fa97b7319 100644 --- a/compiler/testData/diagnostics/tests/Varargs.jet +++ b/compiler/testData/diagnostics/tests/Varargs.jet @@ -13,5 +13,5 @@ fun test() { v1({}, 1, {}) v1({}, {}, {it}) v1({}) {} - v1 {} + v1 {} } \ No newline at end of file