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.JetClassOrObject;
027    import org.jetbrains.jet.lang.psi.JetDeclaration;
028    import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
029    import org.jetbrains.jet.lang.resolve.name.Name;
030    
031    import javax.inject.Inject;
032    import java.util.Collection;
033    import java.util.Map;
034    import java.util.Set;
035    
036    public class OverloadResolver {
037        private TopDownAnalysisContext context;
038        private BindingTrace trace;
039    
040    
041        @Inject
042        public void setContext(TopDownAnalysisContext context) {
043            this.context = context;
044        }
045    
046        @Inject
047        public void setTrace(BindingTrace trace) {
048            this.trace = trace;
049        }
050    
051    
052    
053        public void process() {
054            checkOverloads();
055        }
056    
057        private void checkOverloads() {
058            Pair<MultiMap<ClassDescriptor, ConstructorDescriptor>, MultiMap<Key, ConstructorDescriptor>> pair = constructorsGrouped();
059            MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = pair.first;
060            MultiMap<Key, ConstructorDescriptor> inNamespaces = pair.second;
061    
062            for (Map.Entry<JetClassOrObject, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
063                checkOverloadsInAClass(entry.getValue(), entry.getKey(), inClasses.get(entry.getValue()));
064            }
065            checkOverloadsInANamespace(inNamespaces);
066        }
067    
068        private static class Key extends Pair<String, Name> {
069            Key(String namespace, Name name) {
070                super(namespace, name);
071            }
072            
073            Key(NamespaceDescriptor namespaceDescriptor, Name name) {
074                this(DescriptorUtils.getFQName(namespaceDescriptor).asString(), name);
075            }
076    
077            public String getNamespace() {
078                return first;
079            }
080    
081            public Name getFunctionName() {
082                return second;
083            }
084        }
085    
086        
087        private Pair<MultiMap<ClassDescriptor, ConstructorDescriptor>, MultiMap<Key, ConstructorDescriptor>>
088                constructorsGrouped()
089        {
090            MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = MultiMap.create();
091            MultiMap<Key, ConstructorDescriptor> inNamespaces = MultiMap.create();
092    
093            for (MutableClassDescriptor klass : context.getClasses().values()) {
094                if (klass.getKind().isSingleton()) {
095                    // Constructors of singletons aren't callable from the code, so they shouldn't participate in overload name checking
096                    continue;
097                }
098                DeclarationDescriptor containingDeclaration = klass.getContainingDeclaration();
099                if (containingDeclaration instanceof NamespaceDescriptor) {
100                    NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
101                    inNamespaces.put(new Key(namespaceDescriptor, klass.getName()), klass.getConstructors());
102                }
103                else if (containingDeclaration instanceof ClassDescriptor) {
104                    ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
105                    inClasses.put(classDescriptor, klass.getConstructors());
106                }
107                else if (!(containingDeclaration instanceof FunctionDescriptor)) {
108                    throw new IllegalStateException();
109                }
110            }
111            
112            return Pair.create(inClasses, inNamespaces);
113        }
114    
115        private void checkOverloadsInANamespace(MultiMap<Key, ConstructorDescriptor> inNamespaces) {
116    
117            MultiMap<Key, CallableMemberDescriptor> functionsByName = MultiMap.create();
118    
119            for (SimpleFunctionDescriptor function : context.getFunctions().values()) {
120                DeclarationDescriptor containingDeclaration = function.getContainingDeclaration();
121                if (containingDeclaration instanceof NamespaceDescriptor) {
122                    NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
123                    functionsByName.putValue(new Key(namespaceDescriptor, function.getName()), function);
124                }
125            }
126            
127            for (PropertyDescriptor property : context.getProperties().values()) {
128                DeclarationDescriptor containingDeclaration = property.getContainingDeclaration();
129                if (containingDeclaration instanceof NamespaceDescriptor) {
130                    NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
131                    functionsByName.putValue(new Key(namespaceDescriptor, property.getName()), property);
132                }
133            }
134            
135            for (Map.Entry<Key, Collection<ConstructorDescriptor>> entry : inNamespaces.entrySet()) {
136                functionsByName.putValues(entry.getKey(), entry.getValue());
137            }
138    
139            for (Map.Entry<Key, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
140                checkOverloadsWithSameName(e.getKey().getFunctionName(), e.getValue(), e.getKey().getNamespace());
141            }
142        }
143    
144        private String nameForErrorMessage(ClassDescriptor classDescriptor, JetClassOrObject jetClass) {
145            String name = jetClass.getName();
146            if (name != null) {
147                return name;
148            }
149            if (jetClass instanceof JetObjectDeclaration) {
150                // must be class object
151                name = classDescriptor.getContainingDeclaration().getName().asString();
152                return "class object " + name;
153            }
154            // safe
155            return "<unknown>";
156        }
157    
158        private void checkOverloadsInAClass(
159                MutableClassDescriptor classDescriptor, JetClassOrObject klass,
160                Collection<ConstructorDescriptor> nestedClassConstructors
161        ) {
162            MultiMap<Name, CallableMemberDescriptor> functionsByName = MultiMap.create();
163            
164            if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
165                MutableClassDescriptor classObjectDescriptor = (MutableClassDescriptor) classDescriptor.getClassObjectDescriptor();
166                assert classObjectDescriptor != null;
167                for (CallableMemberDescriptor memberDescriptor : classObjectDescriptor.getDeclaredCallableMembers()) {
168                    functionsByName.putValue(memberDescriptor.getName(), memberDescriptor);
169                }
170            }
171    
172            for (CallableMemberDescriptor function : classDescriptor.getDeclaredCallableMembers()) {
173                functionsByName.putValue(function.getName(), function);
174            }
175            
176            for (ConstructorDescriptor nestedClassConstructor : nestedClassConstructors) {
177                functionsByName.putValue(nestedClassConstructor.getContainingDeclaration().getName(), nestedClassConstructor);
178            }
179            
180            for (Map.Entry<Name, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
181                checkOverloadsWithSameName(e.getKey(), e.getValue(), nameForErrorMessage(classDescriptor, klass));
182            }
183    
184            // Kotlin has no secondary constructors at this time
185    
186        }
187        
188        private void checkOverloadsWithSameName(@NotNull Name name, Collection<CallableMemberDescriptor> functions, @NotNull String functionContainer) {
189            if (functions.size() == 1) {
190                // microoptimization
191                return;
192            }
193            Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = findRedeclarations(functions);
194            reportRedeclarations(functionContainer, redeclarations);
195        }
196    
197        @NotNull
198        private Set<Pair<JetDeclaration, CallableMemberDescriptor>> findRedeclarations(@NotNull Collection<CallableMemberDescriptor> functions) {
199            Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = Sets.newHashSet();
200            for (CallableMemberDescriptor member : functions) {
201                for (CallableMemberDescriptor member2 : functions) {
202                    if (member == member2) {
203                        continue;
204                    }
205    
206                    OverloadUtil.OverloadCompatibilityInfo overloadable = OverloadUtil.isOverloadable(member, member2);
207                    if (!overloadable.isSuccess()) {
208                        JetDeclaration jetDeclaration = (JetDeclaration) BindingContextUtils
209                                .descriptorToDeclaration(trace.getBindingContext(), member);
210                        if (jetDeclaration != null) {
211                            redeclarations.add(Pair.create(jetDeclaration, member));
212                        }
213                    }
214                }
215            }
216            return redeclarations;
217        }
218    
219        private void reportRedeclarations(@NotNull String functionContainer,
220                @NotNull Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations) {
221            for (Pair<JetDeclaration, CallableMemberDescriptor> redeclaration : redeclarations) {
222                CallableMemberDescriptor memberDescriptor = redeclaration.getSecond();
223                JetDeclaration jetDeclaration = redeclaration.getFirst();
224                if (memberDescriptor instanceof PropertyDescriptor) {
225                    trace.report(Errors.REDECLARATION.on(jetDeclaration, memberDescriptor.getName().asString()));
226                }
227                else {
228                    trace.report(Errors.CONFLICTING_OVERLOADS.on(jetDeclaration, memberDescriptor, functionContainer));
229                }
230            }
231        }
232    }