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
092 MultiMap<FqNameUnsafe, CallableMemberDescriptor> functionsByName = MultiMap.create();
093
094 for (SimpleFunctionDescriptor function : c.getFunctions().values()) {
095 if (function.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
096 functionsByName.putValue(getFqName(function), function);
097 }
098 }
099
100 for (PropertyDescriptor property : c.getProperties().values()) {
101 if (property.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
102 functionsByName.putValue(getFqName(property), property);
103 }
104 }
105
106 for (Map.Entry<FqNameUnsafe, Collection<ConstructorDescriptor>> entry : inPackages.entrySet()) {
107 functionsByName.putValues(entry.getKey(), entry.getValue());
108 }
109
110 for (Map.Entry<FqNameUnsafe, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
111 // TODO: don't render FQ name here, extract this logic to somewhere
112 FqNameUnsafe fqName = e.getKey().parent();
113 checkOverloadsWithSameName(e.getValue(), fqName.isRoot() ? "root package" : fqName.asString());
114 }
115 }
116
117 private static String nameForErrorMessage(ClassDescriptor classDescriptor, KtClassOrObject jetClass) {
118 String name = jetClass.getName();
119 if (name != null) {
120 return name;
121 }
122 if (jetClass instanceof KtObjectDeclaration) {
123 // must be companion object
124 name = classDescriptor.getContainingDeclaration().getName().asString();
125 return "companion object " + name;
126 }
127 // safe
128 return "<unknown>";
129 }
130
131 private void checkOverloadsInAClass(
132 ClassDescriptorWithResolutionScopes classDescriptor, KtClassOrObject klass,
133 Collection<ConstructorDescriptor> nestedClassConstructors
134 ) {
135 MultiMap<Name, CallableMemberDescriptor> functionsByName = MultiMap.create();
136
137 for (CallableMemberDescriptor function : classDescriptor.getDeclaredCallableMembers()) {
138 functionsByName.putValue(function.getName(), function);
139 }
140
141 for (ConstructorDescriptor nestedClassConstructor : nestedClassConstructors) {
142 functionsByName.putValue(nestedClassConstructor.getContainingDeclaration().getName(), nestedClassConstructor);
143 }
144
145 for (Map.Entry<Name, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
146 checkOverloadsWithSameName(e.getValue(), nameForErrorMessage(classDescriptor, klass));
147 }
148 }
149
150 private void checkOverloadsWithSameName(
151 Collection<CallableMemberDescriptor> functions,
152 @NotNull String functionContainer
153 ) {
154 if (functions.size() == 1) {
155 // micro-optimization
156 return;
157 }
158 reportRedeclarations(functionContainer, findRedeclarations(functions));
159 }
160
161 @NotNull
162 private Set<Pair<KtDeclaration, CallableMemberDescriptor>> findRedeclarations(@NotNull Collection<CallableMemberDescriptor> functions) {
163 Set<Pair<KtDeclaration, CallableMemberDescriptor>> redeclarations = Sets.newLinkedHashSet();
164 for (CallableMemberDescriptor member : functions) {
165 for (CallableMemberDescriptor member2 : functions) {
166 if (member == member2 || isConstructorsOfDifferentRedeclaredClasses(member, member2)) {
167 continue;
168 }
169
170 OverloadUtil.OverloadCompatibilityInfo overloadable = OverloadUtil.isOverloadable(member, member2);
171 if (!overloadable.isSuccess() && member.getKind() != CallableMemberDescriptor.Kind.SYNTHESIZED) {
172 if (isTopLevelMainInDifferentFiles(member, member2)) {
173 continue;
174 }
175
176 KtDeclaration ktDeclaration = (KtDeclaration) DescriptorToSourceUtils.descriptorToDeclaration(member);
177 if (ktDeclaration != null) {
178 redeclarations.add(Pair.create(ktDeclaration, member));
179 }
180 }
181 }
182 }
183 return redeclarations;
184 }
185
186 private static boolean isConstructorsOfDifferentRedeclaredClasses(
187 @NotNull CallableMemberDescriptor member, @NotNull CallableMemberDescriptor member2
188 ) {
189 if (!(member instanceof ConstructorDescriptor) || !(member2 instanceof ConstructorDescriptor)) return false;
190 // ignore conflicting overloads for constructors of different classes because their redeclarations will be reported
191 // but don't ignore if there's possibility that classes redeclarations will not be reported
192 // (e.g. they're declared in different packages)
193 assert member.getContainingDeclaration().getContainingDeclaration() != null : "Grandparent of constructor should not be null";
194 return member.getContainingDeclaration() != member2.getContainingDeclaration() &&
195 member.getContainingDeclaration().getContainingDeclaration().equals(member2.getContainingDeclaration().getContainingDeclaration());
196 }
197
198 private boolean isTopLevelMainInDifferentFiles(@NotNull CallableMemberDescriptor member, @NotNull CallableMemberDescriptor member2) {
199 if (!DescriptorToSourceUtils.isTopLevelMainFunction(member, mainFunctionDetector) ||
200 !DescriptorToSourceUtils.isTopLevelMainFunction(member2, mainFunctionDetector)) {
201 return false;
202 }
203
204 return DescriptorToSourceUtils.getContainingFile(member) != DescriptorToSourceUtils.getContainingFile(member2);
205 }
206
207 private void reportRedeclarations(@NotNull String functionContainer,
208 @NotNull Set<Pair<KtDeclaration, CallableMemberDescriptor>> redeclarations) {
209 for (Pair<KtDeclaration, CallableMemberDescriptor> redeclaration : redeclarations) {
210 CallableMemberDescriptor memberDescriptor = redeclaration.getSecond();
211
212 KtDeclaration ktDeclaration = redeclaration.getFirst();
213 if (memberDescriptor instanceof PropertyDescriptor) {
214 trace.report(Errors.REDECLARATION.on(ktDeclaration, memberDescriptor.getName().asString()));
215 }
216 else {
217 String containingClassName = ktDeclaration instanceof KtSecondaryConstructor ?
218 ((KtSecondaryConstructor) ktDeclaration).getContainingClassOrObject().getName() : null;
219
220 trace.report(Errors.CONFLICTING_OVERLOADS.on(
221 ktDeclaration, memberDescriptor,
222 containingClassName != null ? containingClassName : functionContainer));
223 }
224 }
225 }
226 }