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 }