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