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.diagnostics.Errors;
025    import org.jetbrains.jet.lang.psi.JetClassOrObject;
026    import org.jetbrains.jet.lang.psi.JetDeclaration;
027    import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
028    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
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    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
037    
038    public class OverloadResolver {
039        private BindingTrace trace;
040    
041        @Inject
042        public void setTrace(BindingTrace trace) {
043            this.trace = trace;
044        }
045    
046        public void process(@NotNull BodiesResolveContext c) {
047            checkOverloads(c);
048        }
049    
050        private void checkOverloads(@NotNull BodiesResolveContext c) {
051            MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = MultiMap.create();
052            MultiMap<FqNameUnsafe, ConstructorDescriptor> inPackages = MultiMap.create();
053            fillGroupedConstructors(c, inClasses, inPackages);
054    
055            for (Map.Entry<JetClassOrObject, ClassDescriptorWithResolutionScopes> entry : c.getDeclaredClasses().entrySet()) {
056                checkOverloadsInAClass(entry.getValue(), entry.getKey(), inClasses.get(entry.getValue()));
057            }
058            checkOverloadsInPackages(c, inPackages);
059        }
060    
061        private static void fillGroupedConstructors(
062                @NotNull BodiesResolveContext c,
063                @NotNull MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses,
064                @NotNull MultiMap<FqNameUnsafe, ConstructorDescriptor> inPackages
065        ) {
066            for (ClassDescriptorWithResolutionScopes klass : c.getDeclaredClasses().values()) {
067                if (klass.getKind().isSingleton()) {
068                    // Constructors of singletons aren't callable from the code, so they shouldn't participate in overload name checking
069                    continue;
070                }
071                DeclarationDescriptor containingDeclaration = klass.getContainingDeclaration();
072                if (containingDeclaration instanceof ClassDescriptor) {
073                    ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
074                    inClasses.put(classDescriptor, klass.getConstructors());
075                }
076                else if (containingDeclaration instanceof PackageFragmentDescriptor) {
077                    inPackages.put(getFqName(klass), klass.getConstructors());
078                }
079                else if (!(containingDeclaration instanceof FunctionDescriptor)) {
080                    throw new IllegalStateException();
081                }
082            }
083        }
084    
085        private void checkOverloadsInPackages(
086                @NotNull BodiesResolveContext c,
087                @NotNull MultiMap<FqNameUnsafe, ConstructorDescriptor> inPackages
088        ) {
089    
090            MultiMap<FqNameUnsafe, CallableMemberDescriptor> functionsByName = MultiMap.create();
091    
092            for (SimpleFunctionDescriptor function : c.getFunctions().values()) {
093                if (function.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
094                    functionsByName.putValue(getFqName(function), function);
095                }
096            }
097            
098            for (PropertyDescriptor property : c.getProperties().values()) {
099                if (property.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
100                    functionsByName.putValue(getFqName(property), property);
101                }
102            }
103            
104            for (Map.Entry<FqNameUnsafe, Collection<ConstructorDescriptor>> entry : inPackages.entrySet()) {
105                functionsByName.putValues(entry.getKey(), entry.getValue());
106            }
107    
108            for (Map.Entry<FqNameUnsafe, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
109                // TODO: don't render FQ name here, extract this logic to somewhere
110                FqNameUnsafe fqName = e.getKey().parent();
111                checkOverloadsWithSameName(e.getValue(), fqName.isRoot() ? "root package" : fqName.asString());
112            }
113        }
114    
115        private static String nameForErrorMessage(ClassDescriptor classDescriptor, JetClassOrObject jetClass) {
116            String name = jetClass.getName();
117            if (name != null) {
118                return name;
119            }
120            if (jetClass instanceof JetObjectDeclaration) {
121                // must be class object
122                name = classDescriptor.getContainingDeclaration().getName().asString();
123                return "class object " + name;
124            }
125            // safe
126            return "<unknown>";
127        }
128    
129        private void checkOverloadsInAClass(
130                ClassDescriptorWithResolutionScopes classDescriptor, JetClassOrObject klass,
131                Collection<ConstructorDescriptor> nestedClassConstructors
132        ) {
133            MultiMap<Name, CallableMemberDescriptor> functionsByName = MultiMap.create();
134            
135            if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
136                ClassDescriptorWithResolutionScopes classObjectDescriptor = classDescriptor.getClassObjectDescriptor();
137                assert classObjectDescriptor != null;
138                for (CallableMemberDescriptor memberDescriptor : classObjectDescriptor.getDeclaredCallableMembers()) {
139                    functionsByName.putValue(memberDescriptor.getName(), memberDescriptor);
140                }
141            }
142    
143            for (CallableMemberDescriptor function : classDescriptor.getDeclaredCallableMembers()) {
144                functionsByName.putValue(function.getName(), function);
145            }
146            
147            for (ConstructorDescriptor nestedClassConstructor : nestedClassConstructors) {
148                functionsByName.putValue(nestedClassConstructor.getContainingDeclaration().getName(), nestedClassConstructor);
149            }
150            
151            for (Map.Entry<Name, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
152                checkOverloadsWithSameName(e.getValue(), nameForErrorMessage(classDescriptor, klass));
153            }
154    
155            // Kotlin has no secondary constructors at this time
156    
157        }
158        
159        private void checkOverloadsWithSameName(
160                Collection<CallableMemberDescriptor> functions,
161                @NotNull String functionContainer
162        ) {
163            if (functions.size() == 1) {
164                // micro-optimization
165                return;
166            }
167            reportRedeclarations(functionContainer, findRedeclarations(functions));
168        }
169    
170        @NotNull
171        private Set<Pair<JetDeclaration, CallableMemberDescriptor>> findRedeclarations(@NotNull Collection<CallableMemberDescriptor> functions) {
172            Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = Sets.newLinkedHashSet();
173            for (CallableMemberDescriptor member : functions) {
174                for (CallableMemberDescriptor member2 : functions) {
175                    if (member == member2) {
176                        continue;
177                    }
178    
179                    OverloadUtil.OverloadCompatibilityInfo overloadable = OverloadUtil.isOverloadable(member, member2);
180                    if (!overloadable.isSuccess()) {
181                        JetDeclaration jetDeclaration = (JetDeclaration) BindingContextUtils
182                                .descriptorToDeclaration(trace.getBindingContext(), member);
183                        if (jetDeclaration != null) {
184                            redeclarations.add(Pair.create(jetDeclaration, member));
185                        }
186                    }
187                }
188            }
189            return redeclarations;
190        }
191    
192        private void reportRedeclarations(@NotNull String functionContainer,
193                @NotNull Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations) {
194            for (Pair<JetDeclaration, CallableMemberDescriptor> redeclaration : redeclarations) {
195                CallableMemberDescriptor memberDescriptor = redeclaration.getSecond();
196                JetDeclaration jetDeclaration = redeclaration.getFirst();
197                if (memberDescriptor instanceof PropertyDescriptor) {
198                    trace.report(Errors.REDECLARATION.on(jetDeclaration, memberDescriptor.getName().asString()));
199                }
200                else {
201                    trace.report(Errors.CONFLICTING_OVERLOADS.on(jetDeclaration, memberDescriptor, functionContainer));
202                }
203            }
204        }
205    }