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 031 import javax.inject.Inject; 032 import java.util.Collection; 033 import java.util.Map; 034 import java.util.Set; 035 036 import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName; 037 038 public class OverloadResolver { 039 private BindingTrace trace; 040 041 @Inject 042 public void setTrace(BindingTrace trace) { 043 this.trace = trace; 044 } 045 046 public void process(@NotNull BodiesResolveContext c) { 047 checkOverloads(c); 048 } 049 050 private void checkOverloads(@NotNull BodiesResolveContext c) { 051 MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses = MultiMap.create(); 052 MultiMap<FqNameUnsafe, ConstructorDescriptor> inPackages = MultiMap.create(); 053 fillGroupedConstructors(c, inClasses, inPackages); 054 055 for (Map.Entry<JetClassOrObject, ClassDescriptorWithResolutionScopes> entry : c.getDeclaredClasses().entrySet()) { 056 checkOverloadsInAClass(entry.getValue(), entry.getKey(), inClasses.get(entry.getValue())); 057 } 058 checkOverloadsInPackages(c, inPackages); 059 } 060 061 private static void fillGroupedConstructors( 062 @NotNull BodiesResolveContext c, 063 @NotNull MultiMap<ClassDescriptor, ConstructorDescriptor> inClasses, 064 @NotNull MultiMap<FqNameUnsafe, ConstructorDescriptor> inPackages 065 ) { 066 for (ClassDescriptorWithResolutionScopes klass : c.getDeclaredClasses().values()) { 067 if (klass.getKind().isSingleton()) { 068 // Constructors of singletons aren't callable from the code, so they shouldn't participate in overload name checking 069 continue; 070 } 071 DeclarationDescriptor containingDeclaration = klass.getContainingDeclaration(); 072 if (containingDeclaration instanceof ClassDescriptor) { 073 ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; 074 inClasses.put(classDescriptor, klass.getConstructors()); 075 } 076 else if (containingDeclaration instanceof PackageFragmentDescriptor) { 077 inPackages.put(getFqName(klass), klass.getConstructors()); 078 } 079 else if (containingDeclaration instanceof ScriptDescriptor) { 080 // TODO: check overload conflicts of functions with constructors in scripts 081 } 082 else if (!(containingDeclaration instanceof FunctionDescriptor)) { 083 throw new IllegalStateException("Illegal class container: " + containingDeclaration); 084 } 085 } 086 } 087 088 private void checkOverloadsInPackages( 089 @NotNull BodiesResolveContext c, 090 @NotNull MultiMap<FqNameUnsafe, ConstructorDescriptor> inPackages 091 ) { 092 093 MultiMap<FqNameUnsafe, CallableMemberDescriptor> functionsByName = MultiMap.create(); 094 095 for (SimpleFunctionDescriptor function : c.getFunctions().values()) { 096 if (function.getContainingDeclaration() instanceof PackageFragmentDescriptor) { 097 functionsByName.putValue(getFqName(function), function); 098 } 099 } 100 101 for (PropertyDescriptor property : c.getProperties().values()) { 102 if (property.getContainingDeclaration() instanceof PackageFragmentDescriptor) { 103 functionsByName.putValue(getFqName(property), property); 104 } 105 } 106 107 for (Map.Entry<FqNameUnsafe, Collection<ConstructorDescriptor>> entry : inPackages.entrySet()) { 108 functionsByName.putValues(entry.getKey(), entry.getValue()); 109 } 110 111 for (Map.Entry<FqNameUnsafe, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) { 112 // TODO: don't render FQ name here, extract this logic to somewhere 113 FqNameUnsafe fqName = e.getKey().parent(); 114 checkOverloadsWithSameName(e.getValue(), fqName.isRoot() ? "root package" : fqName.asString()); 115 } 116 } 117 118 private static String nameForErrorMessage(ClassDescriptor classDescriptor, JetClassOrObject jetClass) { 119 String name = jetClass.getName(); 120 if (name != null) { 121 return name; 122 } 123 if (jetClass instanceof JetObjectDeclaration) { 124 // must be default object 125 name = classDescriptor.getContainingDeclaration().getName().asString(); 126 return "class object " + name; 127 } 128 // safe 129 return "<unknown>"; 130 } 131 132 private void checkOverloadsInAClass( 133 ClassDescriptorWithResolutionScopes classDescriptor, JetClassOrObject klass, 134 Collection<ConstructorDescriptor> nestedClassConstructors 135 ) { 136 MultiMap<Name, CallableMemberDescriptor> functionsByName = MultiMap.create(); 137 138 for (CallableMemberDescriptor function : classDescriptor.getDeclaredCallableMembers()) { 139 functionsByName.putValue(function.getName(), function); 140 } 141 142 for (ConstructorDescriptor nestedClassConstructor : nestedClassConstructors) { 143 functionsByName.putValue(nestedClassConstructor.getContainingDeclaration().getName(), nestedClassConstructor); 144 } 145 146 for (Map.Entry<Name, Collection<CallableMemberDescriptor>> e : functionsByName.entrySet()) { 147 checkOverloadsWithSameName(e.getValue(), nameForErrorMessage(classDescriptor, klass)); 148 } 149 150 // Kotlin has no secondary constructors at this time 151 152 } 153 154 private void checkOverloadsWithSameName( 155 Collection<CallableMemberDescriptor> functions, 156 @NotNull String functionContainer 157 ) { 158 if (functions.size() == 1) { 159 // micro-optimization 160 return; 161 } 162 reportRedeclarations(functionContainer, findRedeclarations(functions)); 163 } 164 165 @NotNull 166 private Set<Pair<JetDeclaration, CallableMemberDescriptor>> findRedeclarations(@NotNull Collection<CallableMemberDescriptor> functions) { 167 Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations = Sets.newLinkedHashSet(); 168 for (CallableMemberDescriptor member : functions) { 169 for (CallableMemberDescriptor member2 : functions) { 170 if (member == member2) { 171 continue; 172 } 173 174 OverloadUtil.OverloadCompatibilityInfo overloadable = OverloadUtil.isOverloadable(member, member2); 175 if (!overloadable.isSuccess() && member.getKind() != CallableMemberDescriptor.Kind.SYNTHESIZED) { 176 JetDeclaration jetDeclaration = (JetDeclaration) DescriptorToSourceUtils.descriptorToDeclaration(member); 177 if (jetDeclaration != null) { 178 redeclarations.add(Pair.create(jetDeclaration, member)); 179 } 180 } 181 } 182 } 183 return redeclarations; 184 } 185 186 private void reportRedeclarations(@NotNull String functionContainer, 187 @NotNull Set<Pair<JetDeclaration, CallableMemberDescriptor>> redeclarations) { 188 for (Pair<JetDeclaration, CallableMemberDescriptor> redeclaration : redeclarations) { 189 CallableMemberDescriptor memberDescriptor = redeclaration.getSecond(); 190 JetDeclaration jetDeclaration = redeclaration.getFirst(); 191 if (memberDescriptor instanceof PropertyDescriptor) { 192 trace.report(Errors.REDECLARATION.on(jetDeclaration, memberDescriptor.getName().asString())); 193 } 194 else { 195 trace.report(Errors.CONFLICTING_OVERLOADS.on(jetDeclaration, memberDescriptor, functionContainer)); 196 } 197 } 198 } 199 }