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