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