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