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    
017    package org.jetbrains.jet.lang.resolve;
018    
019    import com.google.common.collect.Sets;
020    import com.intellij.openapi.util.Pair;
021    import com.intellij.util.containers.MultiMap;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
025    import org.jetbrains.jet.lang.diagnostics.Errors;
026    import org.jetbrains.jet.lang.psi.JetClass;
027    import org.jetbrains.jet.lang.psi.JetClassOrObject;
028    import org.jetbrains.jet.lang.psi.JetDeclaration;
029    import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    
032    import javax.inject.Inject;
033    import java.util.Collection;
034    import java.util.Map;
035    import java.util.Set;
036    
037    public 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    }