提交 5218246d 编写于 作者: S Stepan Koltsov

KT-424: Check declarations for overload-compatibility

===
fun a() {}
fun a() {}
===

must be an error.
上级 1e3f2889
......@@ -375,6 +375,19 @@ public interface Errors {
}
};
ParameterizedDiagnosticFactory2<JetClassOrObject, CallableMemberDescriptor> CONFLICTING_OVERLOADS = new ParameterizedDiagnosticFactory2<JetClassOrObject, CallableMemberDescriptor>(ERROR, "{1} is already defined in ''{0}''") {
@Override
protected String makeMessageForA(@NotNull JetClassOrObject jetClassOrObject) {
return jetClassOrObject.getName();
}
@Override
protected String makeMessageForB(@NotNull CallableMemberDescriptor memberDescriptor) {
return DescriptorRenderer.TEXT.render(memberDescriptor);
}
};
ParameterizedDiagnosticFactory3<String, JetType, JetType> RESULT_TYPE_MISMATCH = ParameterizedDiagnosticFactory3.create(ERROR, "{0} must return {1} but returns {2}");
ParameterizedDiagnosticFactory3<String, String, String> UNSAFE_INFIX_CALL = ParameterizedDiagnosticFactory3.create(ERROR, "Infix call corresponds to a dot-qualified call ''{0}.{1}({2})'' which is not allowed on a nullable receiver ''{0}''. Use '?.'-qualified call instead");
......
package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.MutableClassDescriptor;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.jetbrains.jet.lang.resolve.BindingContext.DELEGATED;
/**
* @author Stepan Koltsov
*/
public class OverloadResolver {
private final TopDownAnalysisContext context;
public OverloadResolver(@NotNull TopDownAnalysisContext context) {
this.context = context;
}
public void process() {
checkOverloads();
}
private void checkOverloads() {
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
checkOverloadsInAClass(entry.getValue(), entry.getKey());
}
for (Map.Entry<JetObjectDeclaration, MutableClassDescriptor> entry : context.getObjects().entrySet()) {
checkOverloadsInAClass(entry.getValue(), entry.getKey());
}
}
private void checkOverloadsInAClass(MutableClassDescriptor classDescriptor, JetClassOrObject klass) {
MultiMap<String, FunctionDescriptor> functionsByName = MultiMap.create();
for (FunctionDescriptor function : classDescriptor.getFunctions()) {
functionsByName.putValue(function.getName(), function);
}
for (Map.Entry<String, Collection<FunctionDescriptor>> e : functionsByName.entrySet()) {
checkOverloadsWithSameName(e.getKey(), e.getValue(), klass);
}
// properties are checked elsewhere
// Kotlin has no secondary constructors at this time
}
private void checkOverloadsWithSameName(String name, Collection<FunctionDescriptor> functions, JetClassOrObject klass) {
if (functions.size() == 1) {
// microoptimization
return;
}
for (FunctionDescriptor function : functions) {
for (FunctionDescriptor function2 : functions) {
if (function == function2) {
continue;
}
OverloadUtil.OverloadCompatibilityInfo overloadble = OverloadUtil.isOverloadble(function, function2);
if (!overloadble.isSuccess()) {
JetNamedFunction member = (JetNamedFunction) context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, function);
if (member == null) {
assert context.getTrace().get(DELEGATED, function);
return;
}
// XXX: mark "fun" keyword and parameters as in Java IDE // stepan.koltsov@
PsiElement mark = member.getNameIdentifier();
context.getTrace().report(Errors.CONFLICTING_OVERLOADS.on(mark, klass, function));
}
}
}
}
}
package org.jetbrains.jet.lang.resolve;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
/**
* @author Stepan Koltsov
*/
public class OverloadUtil {
public static OverloadCompatibilityInfo isOverloadble(FunctionDescriptor a, FunctionDescriptor b) {
OverridingUtil.OverrideCompatibilityInfo overrideCompatibilityInfo = OverridingUtil.isOverridableByImpl(a, b, false);
if (overrideCompatibilityInfo.isSuccess()) {
return OverloadCompatibilityInfo.someError();
} else {
return OverloadCompatibilityInfo.success();
}
}
public static class OverloadCompatibilityInfo {
private static final OverloadCompatibilityInfo SUCCESS = new OverloadCompatibilityInfo(true, "SUCCESS");
public static OverloadCompatibilityInfo success() {
return SUCCESS;
}
public static OverloadCompatibilityInfo someError() {
return new OverloadCompatibilityInfo(false, "XXX");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private final boolean isSuccess;
private final String message;
public OverloadCompatibilityInfo(boolean success, String message) {
isSuccess = success;
this.message = message;
}
public boolean isSuccess() {
return isSuccess;
}
public String getMessage() {
return message;
}
}
}
......@@ -93,6 +93,13 @@ public class OverridingUtil {
@NotNull
public static OverrideCompatibilityInfo isOverridableBy(@NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
return isOverridableByImpl(superDescriptor, subDescriptor, true);
}
/**
* @param forOverride true for override, false for overload
*/
static OverrideCompatibilityInfo isOverridableByImpl(CallableDescriptor superDescriptor, CallableDescriptor subDescriptor, boolean forOverride) {
if (superDescriptor instanceof FunctionDescriptor) {
if (subDescriptor instanceof PropertyDescriptor) return OverrideCompatibilityInfo.memberKindMismatch();
}
......
......@@ -52,6 +52,7 @@ public class TopDownAnalyzer {
new DeclarationResolver(context).process();
new DelegationResolver(context).process();
new OverrideResolver(context).process();
new OverloadResolver(context).process();
new BodyResolver(context).resolveBehaviorDeclarationBodies();
new ControlFlowAnalyzer(context, flowDataTraceFactory, declaredLocally).process();
new DeclarationsChecker(context).process();
......
// http://youtrack.jetbrains.net/issue/KT-424
class A {
// XXX: error should be reported on the whole method signature
// not only on method name
fun <!CONFLICTING_OVERLOADS!>a<!>() {
}
fun <!CONFLICTING_OVERLOADS!>a<!>() {
}
}
package org.jetbrains.jet.types;
import org.jetbrains.jet.JetLiteFixture;
import org.jetbrains.jet.JetTestCaseBuilder;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.lang.JetSemanticServices;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver;
import org.jetbrains.jet.lang.resolve.OverloadUtil;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
/**
* @author Stepan Koltsov
*/
public class JetOverloadTest extends JetLiteFixture {
private ModuleDescriptor root = new ModuleDescriptor("test_root");
private JetStandardLibrary library;
private JetSemanticServices semanticServices;
private ClassDescriptorResolver classDescriptorResolver;
@Override
public void setUp() throws Exception {
super.setUp();
library = JetStandardLibrary.getJetStandardLibrary(getProject());
semanticServices = JetSemanticServices.createSemanticServices(library);
classDescriptorResolver = semanticServices.getClassDescriptorResolver(JetTestUtils.DUMMY_TRACE);
}
@Override
protected String getTestDataPath() {
return JetTestCaseBuilder.getTestDataPathBase();
}
public void testBasic() throws Exception {
assertNotOverloadable(
"fun a() : Int",
"fun a() : Int");
assertNotOverloadable(
"fun a() : Int",
"fun a() : Any");
assertNotOverloadable(
"fun a<T1>() : T1",
"fun a<T>() : T");
assertNotOverloadable(
"fun a<T1>(a : T1) : T1",
"fun a<T>(a : T) : T");
assertNotOverloadable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y : T>(a : T) : T");
assertNotOverloadable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y : T>(a : T) : Y");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
assertOverloadable(
"fun ab() : Int",
"fun a() : Int");
assertNotOverloadable(
"fun a() : Int",
"fun a() : Any");
assertOverloadable(
"fun a(a : Int) : Int",
"fun a() : Int");
assertOverloadable(
"fun a() : Int",
"fun a(a : Int) : Int");
assertOverloadable(
"fun a(a : Int?) : Int",
"fun a(a : Int) : Int");
// XXX: different from overridable
/*
assertNotOverloadable(
"fun a<T>(a : Int) : Int",
"fun a(a : Int) : Int");
*/
assertOverloadable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y>(a : T) : T");
assertOverloadable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y : T>(a : Y) : T");
assertNotOverloadable(
"fun a<T1, X : T1>(a : T1) : X",
"fun a<T, Y : T>(a : T) : T");
assertNotOverloadable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<in T>) : T");
assertOverloadable(
"fun a<T1, X : Array<T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<in T>) : T");
assertOverloadable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<in T>>(a : Array<in T>) : T");
assertOverloadable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<*>>(a : Array<in T>) : T");
assertOverloadable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<out T>) : T");
assertOverloadable(
"fun a<T1, X : Array<out T1>>(a : Array<*>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<in T>) : T");
}
private void assertNotOverloadable(String funA, String funB) {
assertOverloadabilityRelation(funA, funB, true);
}
private void assertOverloadable(String funA, String funB) {
assertOverloadabilityRelation(funA, funB, false);
}
private void assertOverloadabilityRelation(String funA, String funB, boolean expectedIsError) {
FunctionDescriptor a = makeFunction(funA);
FunctionDescriptor b = makeFunction(funB);
{
OverloadUtil.OverloadCompatibilityInfo overloadableWith = OverloadUtil.isOverloadble(a, b);
assertEquals(overloadableWith.getMessage(), expectedIsError, !overloadableWith.isSuccess());
}
{
OverloadUtil.OverloadCompatibilityInfo overloadableWith = OverloadUtil.isOverloadble(b, a);
assertEquals(overloadableWith.getMessage(), expectedIsError, !overloadableWith.isSuccess());
}
}
private FunctionDescriptor makeFunction(String funDecl) {
JetNamedFunction function = JetPsiFactory.createFunction(getProject(), funDecl);
return classDescriptorResolver.resolveFunctionDescriptor(root, library.getLibraryScope(), function);
}
}
......@@ -67,6 +67,13 @@ public class JetOverridingTest extends JetLiteFixture {
"fun a() : Int",
"fun a() : Any");
// return types are not cheked in the utility
/*
assertNotOverridable(
"fun a() : Any",
"fun a() : Int");
*/
assertNotOverridable(
"fun a(a : Int) : Int",
"fun a() : Int");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册