001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.lang.resolve;
018
019import com.google.common.collect.Sets;
020import com.intellij.openapi.util.Pair;
021import com.intellij.util.containers.MultiMap;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.jet.lang.descriptors.*;
024import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
025import org.jetbrains.jet.lang.diagnostics.Errors;
026import org.jetbrains.jet.lang.psi.JetClass;
027import org.jetbrains.jet.lang.psi.JetClassOrObject;
028import org.jetbrains.jet.lang.psi.JetDeclaration;
029import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
030import org.jetbrains.jet.lang.resolve.name.Name;
031
032import javax.inject.Inject;
033import java.util.Collection;
034import java.util.Map;
035import java.util.Set;
036
037public class OverloadResolver {
038    private TopDownAnalysisContext context;
039    private BindingTrace trace;
040
041
042    @Inject
043    public void setContext(TopDownAnalysisContext context) {
044        this.context = context;
045    }
046
047    @Inject
048    public void setTrace(BindingTrace trace) {
049        this.trace = trace;
050    }
051
052
053
054    public void process() {
055        checkOverloads();
056    }
057
058    private void checkOverloads() {
059        Pair<MultiMap<ClassDescriptor, ConstructorDescriptor>, MultiMap<Key, ConstructorDescriptor>> pair = constructorsGrouped();
060        MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = pair.first;
061        MultiMap<Key, ConstructorDescriptor> inNamespaces = pair.second;
062
063        for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
064            checkOverloadsInAClass(entry.getValue(), entry.getKey(), inClasses.get(entry.getValue()));
065        }
066        for (Map.Entry<JetObjectDeclaration, MutableClassDescriptor> entry : context.getObjects().entrySet()) {
067            checkOverloadsInAClass(entry.getValue(), entry.getKey(), inClasses.get(entry.getValue()));
068        }
069        checkOverloadsInANamespace(inNamespaces);
070    }
071
072    private static class Key extends Pair<String, Name> {
073        Key(String namespace, Name name) {
074            super(namespace, name);
075        }
076        
077        Key(NamespaceDescriptor namespaceDescriptor, Name name) {
078            this(DescriptorUtils.getFQName(namespaceDescriptor).asString(), name);
079        }
080
081        public String getNamespace() {
082            return first;
083        }
084
085        public Name getFunctionName() {
086            return second;
087        }
088    }
089
090    
091    private Pair<MultiMap<ClassDescriptor, ConstructorDescriptor>, MultiMap<Key, ConstructorDescriptor>>
092            constructorsGrouped()
093    {
094        MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = MultiMap.create();
095        MultiMap<Key, ConstructorDescriptor> inNamespaces = MultiMap.create();
096
097        for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
098            MutableClassDescriptor klass = entry.getValue();
099            DeclarationDescriptor containingDeclaration = klass.getContainingDeclaration();
100            if (containingDeclaration instanceof NamespaceDescriptor) {
101                NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
102                inNamespaces.put(new Key(namespaceDescriptor, klass.getName()), klass.getConstructors());
103            }
104            else if (containingDeclaration instanceof ClassDescriptor) {
105                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
106                inClasses.put(classDescriptor, klass.getConstructors());
107            }
108            else if (!(containingDeclaration instanceof FunctionDescriptor)) {
109                throw new IllegalStateException();
110            }
111        }
112        
113        return Pair.create(inClasses, inNamespaces);
114    }
115
116    private void checkOverloadsInANamespace(MultiMap<Key, ConstructorDescriptor> inNamespaces) {
117
118        MultiMap<Key, CallableMemberDescriptor> functionsByName = MultiMap.create();
119
120        for (SimpleFunctionDescriptor function : context.getFunctions().values()) {
121            DeclarationDescriptor containingDeclaration = function.getContainingDeclaration();
122            if (containingDeclaration instanceof NamespaceDescriptor) {
123                NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
124                functionsByName.putValue(new Key(namespaceDescriptor, function.getName()), function);
125            }
126        }
127        
128        for (PropertyDescriptor property : context.getProperties().values()) {
129            DeclarationDescriptor containingDeclaration = property.getContainingDeclaration();
130            if (containingDeclaration instanceof NamespaceDescriptor) {
131                NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
132                functionsByName.putValue(new Key(namespaceDescriptor, property.getName()), property);
133            }
134        }
135        
136        for (Map.Entry<Key, Collection<ConstructorDescriptor>> entry : inNamespaces.entrySet()) {
137            functionsByName.putValues(entry.getKey(), entry.getValue());
138        }
139
140        for (Map.Entry<Key, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
141            checkOverloadsWithSameName(e.getKey().getFunctionName(), e.getValue(), e.getKey().getNamespace());
142        }
143    }
144
145    private String nameForErrorMessage(ClassDescriptor classDescriptor, JetClassOrObject jetClass) {
146        String name = jetClass.getName();
147        if (name != null) {
148            return name;
149        }
150        if (jetClass instanceof JetObjectDeclaration) {
151            // must be class object
152            name = classDescriptor.getContainingDeclaration().getName().asString();
153            return "class object " + name;
154        }
155        // safe
156        return "<unknown>";
157    }
158
159    private void checkOverloadsInAClass(
160            MutableClassDescriptor classDescriptor, JetClassOrObject klass,
161            Collection<ConstructorDescriptor> nestedClassConstructors
162    ) {
163        MultiMap<Name, CallableMemberDescriptor> functionsByName = MultiMap.create();
164        
165        if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
166            MutableClassDescriptor classObjectDescriptor = (MutableClassDescriptor) classDescriptor.getClassObjectDescriptor();
167            assert classObjectDescriptor != null;
168            for (CallableMemberDescriptor memberDescriptor : classObjectDescriptor.getDeclaredCallableMembers()) {
169                functionsByName.putValue(memberDescriptor.getName(), memberDescriptor);
170            }
171        }
172
173        for (CallableMemberDescriptor function : classDescriptor.getDeclaredCallableMembers()) {
174            functionsByName.putValue(function.getName(), function);
175        }
176        
177        for (ConstructorDescriptor nestedClassConstructor : nestedClassConstructors) {
178            functionsByName.putValue(nestedClassConstructor.getContainingDeclaration().getName(), nestedClassConstructor);
179        }
180        
181        for (Map.Entry<Name, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
182            checkOverloadsWithSameName(e.getKey(), e.getValue(), nameForErrorMessage(classDescriptor, klass));
183        }
184
185        // Kotlin has no secondary constructors at this time
186
187    }
188    
189    private void checkOverloadsWithSameName(@NotNull Name name, Collection<CallableMemberDescriptor> functions, @NotNull String functionContainer) {
190        if (functions.size() == 1) {
191            // microoptimization
192            return;
193        }
194        Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = findRedeclarations(functions);
195        reportRedeclarations(functionContainer, redeclarations);
196    }
197
198    @NotNull
199    private Set<Pair<JetDeclaration, CallableMemberDescriptor>> findRedeclarations(@NotNull Collection<CallableMemberDescriptor> functions) {
200        Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = Sets.newHashSet();
201        for (CallableMemberDescriptor member : functions) {
202            for (CallableMemberDescriptor member2 : functions) {
203                if (member == member2) {
204                    continue;
205                }
206
207                OverloadUtil.OverloadCompatibilityInfo overloadable = OverloadUtil.isOverloadable(member, member2);
208                if (!overloadable.isSuccess()) {
209                    JetDeclaration jetDeclaration = (JetDeclaration) BindingContextUtils
210                            .descriptorToDeclaration(trace.getBindingContext(), member);
211                    if (jetDeclaration != null) {
212                        redeclarations.add(Pair.create(jetDeclaration, member));
213                    }
214                }
215            }
216        }
217        return redeclarations;
218    }
219
220    private void reportRedeclarations(@NotNull String functionContainer,
221            @NotNull Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations) {
222        for (Pair<JetDeclaration, CallableMemberDescriptor> redeclaration : redeclarations) {
223            CallableMemberDescriptor memberDescriptor = redeclaration.getSecond();
224            JetDeclaration jetDeclaration = redeclaration.getFirst();
225            if (memberDescriptor instanceof PropertyDescriptor) {
226                trace.report(Errors.REDECLARATION.on(jetDeclaration, memberDescriptor.getName().asString()));
227            }
228            else {
229                trace.report(Errors.CONFLICTING_OVERLOADS.on(jetDeclaration, memberDescriptor, functionContainer));
230            }
231        }
232    }
233}