001 /*
002 * Copyright 2010-2013 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.jet.lang.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.jet.lang.descriptors.*;
024 import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
025 import org.jetbrains.jet.lang.diagnostics.Errors;
026 import org.jetbrains.jet.lang.psi.JetClassOrObject;
027 import org.jetbrains.jet.lang.psi.JetDeclaration;
028 import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
029 import org.jetbrains.jet.lang.resolve.name.Name;
030
031 import javax.inject.Inject;
032 import java.util.Collection;
033 import java.util.Map;
034 import java.util.Set;
035
036 public class OverloadResolver {
037 private TopDownAnalysisContext context;
038 private BindingTrace trace;
039
040
041 @Inject
042 public void setContext(TopDownAnalysisContext context) {
043 this.context = context;
044 }
045
046 @Inject
047 public void setTrace(BindingTrace trace) {
048 this.trace = trace;
049 }
050
051
052
053 public void process() {
054 checkOverloads();
055 }
056
057 private void checkOverloads() {
058 Pair<MultiMap<ClassDescriptor, ConstructorDescriptor>, MultiMap<Key, ConstructorDescriptor>> pair = constructorsGrouped();
059 MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = pair.first;
060 MultiMap<Key, ConstructorDescriptor> inNamespaces = pair.second;
061
062 for (Map.Entry<JetClassOrObject, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
063 checkOverloadsInAClass(entry.getValue(), entry.getKey(), inClasses.get(entry.getValue()));
064 }
065 checkOverloadsInANamespace(inNamespaces);
066 }
067
068 private static class Key extends Pair<String, Name> {
069 Key(String namespace, Name name) {
070 super(namespace, name);
071 }
072
073 Key(NamespaceDescriptor namespaceDescriptor, Name name) {
074 this(DescriptorUtils.getFQName(namespaceDescriptor).asString(), name);
075 }
076
077 public String getNamespace() {
078 return first;
079 }
080
081 public Name getFunctionName() {
082 return second;
083 }
084 }
085
086
087 private Pair<MultiMap<ClassDescriptor, ConstructorDescriptor>, MultiMap<Key, ConstructorDescriptor>>
088 constructorsGrouped()
089 {
090 MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = MultiMap.create();
091 MultiMap<Key, ConstructorDescriptor> inNamespaces = MultiMap.create();
092
093 for (MutableClassDescriptor klass : context.getClasses().values()) {
094 if (klass.getKind().isSingleton()) {
095 // Constructors of singletons aren't callable from the code, so they shouldn't participate in overload name checking
096 continue;
097 }
098 DeclarationDescriptor containingDeclaration = klass.getContainingDeclaration();
099 if (containingDeclaration instanceof NamespaceDescriptor) {
100 NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
101 inNamespaces.put(new Key(namespaceDescriptor, klass.getName()), klass.getConstructors());
102 }
103 else if (containingDeclaration instanceof ClassDescriptor) {
104 ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
105 inClasses.put(classDescriptor, klass.getConstructors());
106 }
107 else if (!(containingDeclaration instanceof FunctionDescriptor)) {
108 throw new IllegalStateException();
109 }
110 }
111
112 return Pair.create(inClasses, inNamespaces);
113 }
114
115 private void checkOverloadsInANamespace(MultiMap<Key, ConstructorDescriptor> inNamespaces) {
116
117 MultiMap<Key, CallableMemberDescriptor> functionsByName = MultiMap.create();
118
119 for (SimpleFunctionDescriptor function : context.getFunctions().values()) {
120 DeclarationDescriptor containingDeclaration = function.getContainingDeclaration();
121 if (containingDeclaration instanceof NamespaceDescriptor) {
122 NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
123 functionsByName.putValue(new Key(namespaceDescriptor, function.getName()), function);
124 }
125 }
126
127 for (PropertyDescriptor property : context.getProperties().values()) {
128 DeclarationDescriptor containingDeclaration = property.getContainingDeclaration();
129 if (containingDeclaration instanceof NamespaceDescriptor) {
130 NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration;
131 functionsByName.putValue(new Key(namespaceDescriptor, property.getName()), property);
132 }
133 }
134
135 for (Map.Entry<Key, Collection<ConstructorDescriptor>> entry : inNamespaces.entrySet()) {
136 functionsByName.putValues(entry.getKey(), entry.getValue());
137 }
138
139 for (Map.Entry<Key, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
140 checkOverloadsWithSameName(e.getKey().getFunctionName(), e.getValue(), e.getKey().getNamespace());
141 }
142 }
143
144 private String nameForErrorMessage(ClassDescriptor classDescriptor, JetClassOrObject jetClass) {
145 String name = jetClass.getName();
146 if (name != null) {
147 return name;
148 }
149 if (jetClass instanceof JetObjectDeclaration) {
150 // must be class object
151 name = classDescriptor.getContainingDeclaration().getName().asString();
152 return "class object " + name;
153 }
154 // safe
155 return "<unknown>";
156 }
157
158 private void checkOverloadsInAClass(
159 MutableClassDescriptor classDescriptor, JetClassOrObject klass,
160 Collection<ConstructorDescriptor> nestedClassConstructors
161 ) {
162 MultiMap<Name, CallableMemberDescriptor> functionsByName = MultiMap.create();
163
164 if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
165 MutableClassDescriptor classObjectDescriptor = (MutableClassDescriptor) classDescriptor.getClassObjectDescriptor();
166 assert classObjectDescriptor != null;
167 for (CallableMemberDescriptor memberDescriptor : classObjectDescriptor.getDeclaredCallableMembers()) {
168 functionsByName.putValue(memberDescriptor.getName(), memberDescriptor);
169 }
170 }
171
172 for (CallableMemberDescriptor function : classDescriptor.getDeclaredCallableMembers()) {
173 functionsByName.putValue(function.getName(), function);
174 }
175
176 for (ConstructorDescriptor nestedClassConstructor : nestedClassConstructors) {
177 functionsByName.putValue(nestedClassConstructor.getContainingDeclaration().getName(), nestedClassConstructor);
178 }
179
180 for (Map.Entry<Name, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) {
181 checkOverloadsWithSameName(e.getKey(), e.getValue(), nameForErrorMessage(classDescriptor, klass));
182 }
183
184 // Kotlin has no secondary constructors at this time
185
186 }
187
188 private void checkOverloadsWithSameName(@NotNull Name name, Collection<CallableMemberDescriptor> functions, @NotNull String functionContainer) {
189 if (functions.size() == 1) {
190 // microoptimization
191 return;
192 }
193 Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = findRedeclarations(functions);
194 reportRedeclarations(functionContainer, redeclarations);
195 }
196
197 @NotNull
198 private Set<Pair<JetDeclaration, CallableMemberDescriptor>> findRedeclarations(@NotNull Collection<CallableMemberDescriptor> functions) {
199 Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = Sets.newHashSet();
200 for (CallableMemberDescriptor member : functions) {
201 for (CallableMemberDescriptor member2 : functions) {
202 if (member == member2) {
203 continue;
204 }
205
206 OverloadUtil.OverloadCompatibilityInfo overloadable = OverloadUtil.isOverloadable(member, member2);
207 if (!overloadable.isSuccess()) {
208 JetDeclaration jetDeclaration = (JetDeclaration) BindingContextUtils
209 .descriptorToDeclaration(trace.getBindingContext(), member);
210 if (jetDeclaration != null) {
211 redeclarations.add(Pair.create(jetDeclaration, member));
212 }
213 }
214 }
215 }
216 return redeclarations;
217 }
218
219 private void reportRedeclarations(@NotNull String functionContainer,
220 @NotNull Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations) {
221 for (Pair<JetDeclaration, CallableMemberDescriptor> redeclaration : redeclarations) {
222 CallableMemberDescriptor memberDescriptor = redeclaration.getSecond();
223 JetDeclaration jetDeclaration = redeclaration.getFirst();
224 if (memberDescriptor instanceof PropertyDescriptor) {
225 trace.report(Errors.REDECLARATION.on(jetDeclaration, memberDescriptor.getName().asString()));
226 }
227 else {
228 trace.report(Errors.CONFLICTING_OVERLOADS.on(jetDeclaration, memberDescriptor, functionContainer));
229 }
230 }
231 }
232 }