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 017package org.jetbrains.jet.lang.resolve.calls.results; 018 019import com.google.common.collect.Sets; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.jet.lang.descriptors.CallableDescriptor; 022import org.jetbrains.jet.lang.resolve.BindingTrace; 023import org.jetbrains.jet.lang.resolve.OverridingUtil; 024import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; 025import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy; 026 027import java.util.*; 028 029import static org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl.MAP_TO_CANDIDATE; 030import static org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl.MAP_TO_RESULT; 031import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.*; 032 033public class ResolutionResultsHandler { 034 public static ResolutionResultsHandler INSTANCE = new ResolutionResultsHandler(); 035 036 private ResolutionResultsHandler() {} 037 038 @NotNull 039 public <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeResultAndReportErrors( 040 @NotNull BindingTrace trace, 041 @NotNull TracingStrategy tracing, 042 @NotNull Set<ResolvedCallWithTrace<D>> candidates 043 ) { 044 Set<ResolvedCallWithTrace<D>> successfulCandidates = Sets.newLinkedHashSet(); 045 Set<ResolvedCallWithTrace<D>> failedCandidates = Sets.newLinkedHashSet(); 046 Set<ResolvedCallWithTrace<D>> incompleteCandidates = Sets.newLinkedHashSet(); 047 Set<ResolvedCallWithTrace<D>> candidatesWithWrongReceiver = Sets.newLinkedHashSet(); 048 for (ResolvedCallWithTrace<D> candidateCall : candidates) { 049 ResolutionStatus status = candidateCall.getStatus(); 050 assert status != UNKNOWN_STATUS : "No resolution for " + candidateCall.getCandidateDescriptor(); 051 if (status.isSuccess()) { 052 successfulCandidates.add(candidateCall); 053 } 054 else if (status == INCOMPLETE_TYPE_INFERENCE) { 055 incompleteCandidates.add(candidateCall); 056 } 057 else if (candidateCall.getStatus() == RECEIVER_TYPE_ERROR) { 058 candidatesWithWrongReceiver.add(candidateCall); 059 } 060 else if (candidateCall.getStatus() != RECEIVER_PRESENCE_ERROR) { 061 failedCandidates.add(candidateCall); 062 } 063 } 064 // TODO : maybe it's better to filter overrides out first, and only then look for the maximally specific 065 066 if (!successfulCandidates.isEmpty() || !incompleteCandidates.isEmpty()) { 067 return computeSuccessfulResult(trace, tracing, successfulCandidates, incompleteCandidates); 068 } 069 else if (!failedCandidates.isEmpty()) { 070 return computeFailedResult(trace, tracing, failedCandidates); 071 } 072 if (!candidatesWithWrongReceiver.isEmpty()) { 073 tracing.unresolvedReferenceWrongReceiver(trace, candidatesWithWrongReceiver); 074 return OverloadResolutionResultsImpl.candidatesWithWrongReceiver(candidatesWithWrongReceiver); 075 } 076 tracing.unresolvedReference(trace); 077 return OverloadResolutionResultsImpl.nameNotFound(); 078 } 079 080 @NotNull 081 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeSuccessfulResult( 082 BindingTrace trace, 083 TracingStrategy tracing, 084 Set<ResolvedCallWithTrace<D>> successfulCandidates, 085 Set<ResolvedCallWithTrace<D>> incompleteCandidates 086 ) { 087 Set<ResolvedCallWithTrace<D>> successfulAndIncomplete = Sets.newLinkedHashSet(); 088 successfulAndIncomplete.addAll(successfulCandidates); 089 successfulAndIncomplete.addAll(incompleteCandidates); 090 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(successfulAndIncomplete, true); 091 if (results.isSingleResult()) { 092 ResolvedCallWithTrace<D> resultingCall = results.getResultingCall(); 093 resultingCall.getTrace().moveAllMyDataTo(trace); 094 if (resultingCall.getStatus() == INCOMPLETE_TYPE_INFERENCE) { 095 return OverloadResolutionResultsImpl.incompleteTypeInference(resultingCall); 096 } 097 } 098 if (results.isAmbiguity()) { 099 tracing.recordAmbiguity(trace, results.getResultingCalls()); 100 if (allIncomplete(results.getResultingCalls())) { 101 tracing.cannotCompleteResolve(trace, results.getResultingCalls()); 102 return OverloadResolutionResultsImpl.incompleteTypeInference(results.getResultingCalls()); 103 } 104 // This check is needed for the following case: 105 // x.foo(unresolved) -- if there are multiple foo's, we'd report an ambiguity, and it does not make sense here 106 if (allClean(results.getResultingCalls())) { 107 tracing.ambiguity(trace, results.getResultingCalls()); 108 } 109 } 110 return results; 111 } 112 113 @NotNull 114 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeFailedResult( 115 BindingTrace trace, 116 TracingStrategy tracing, 117 Set<ResolvedCallWithTrace<D>> failedCandidates 118 ) { 119 if (failedCandidates.size() != 1) { 120 // This is needed when there are several overloads some of which are OK but for nullability of the receiver, 121 // and some are not OK at all. In this case we'd like to say "unsafe call" rather than "none applicable" 122 // Used to be: weak errors. Generalized for future extensions 123 for (EnumSet<ResolutionStatus> severityLevel : SEVERITY_LEVELS) { 124 Set<ResolvedCallWithTrace<D>> thisLevel = Sets.newLinkedHashSet(); 125 for (ResolvedCallWithTrace<D> candidate : failedCandidates) { 126 if (severityLevel.contains(candidate.getStatus())) { 127 thisLevel.add(candidate); 128 } 129 } 130 if (!thisLevel.isEmpty()) { 131 OverloadResolutionResultsImpl<D> results = chooseAndReportMaximallySpecific(thisLevel, false); 132 if (results.isSingleResult()) { 133 results.getResultingCall().getTrace().moveAllMyDataTo(trace); 134 return OverloadResolutionResultsImpl.singleFailedCandidate(results.getResultingCall()); 135 } 136 137 tracing.noneApplicable(trace, results.getResultingCalls()); 138 tracing.recordAmbiguity(trace, results.getResultingCalls()); 139 return OverloadResolutionResultsImpl.manyFailedCandidates(results.getResultingCalls()); 140 } 141 } 142 143 assert false : "Should not be reachable, cause every status must belong to some level"; 144 145 Set<ResolvedCallWithTrace<D>> noOverrides = OverridingUtil.filterOverrides(failedCandidates, MAP_TO_CANDIDATE); 146 if (noOverrides.size() != 1) { 147 tracing.noneApplicable(trace, noOverrides); 148 tracing.recordAmbiguity(trace, noOverrides); 149 return OverloadResolutionResultsImpl.manyFailedCandidates(noOverrides); 150 } 151 152 failedCandidates = noOverrides; 153 } 154 155 ResolvedCallWithTrace<D> failed = failedCandidates.iterator().next(); 156 failed.getTrace().moveAllMyDataTo(trace); 157 return OverloadResolutionResultsImpl.singleFailedCandidate(failed); 158 } 159 160 private static <D extends CallableDescriptor> boolean allClean(@NotNull Collection<ResolvedCallWithTrace<D>> results) { 161 for (ResolvedCallWithTrace<D> result : results) { 162 if (result.isDirty()) return false; 163 } 164 return true; 165 } 166 167 private static <D extends CallableDescriptor> boolean allIncomplete(@NotNull Collection<ResolvedCallWithTrace<D>> results) { 168 for (ResolvedCallWithTrace<D> result : results) { 169 if (result.getStatus() != INCOMPLETE_TYPE_INFERENCE) return false; 170 } 171 return true; 172 } 173 174 @NotNull 175 private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> chooseAndReportMaximallySpecific( 176 @NotNull Set<ResolvedCallWithTrace<D>> candidates, 177 boolean discriminateGenerics 178 ) { 179 if (candidates.size() == 1) { 180 return OverloadResolutionResultsImpl.success(candidates.iterator().next()); 181 } 182 183 Set<ResolvedCallWithTrace<D>> cleanCandidates = Sets.newLinkedHashSet(candidates); 184 for (Iterator<ResolvedCallWithTrace<D>> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) { 185 ResolvedCallWithTrace<D> candidate = iterator.next(); 186 if (candidate.isDirty()) { 187 iterator.remove(); 188 } 189 } 190 191 if (cleanCandidates.isEmpty()) { 192 cleanCandidates = candidates; 193 } 194 ResolvedCallWithTrace<D> maximallySpecific = OverloadingConflictResolver.INSTANCE.findMaximallySpecific(cleanCandidates, false); 195 if (maximallySpecific != null) { 196 return OverloadResolutionResultsImpl.success(maximallySpecific); 197 } 198 199 if (discriminateGenerics) { 200 ResolvedCallWithTrace<D> maximallySpecificGenericsDiscriminated = OverloadingConflictResolver.INSTANCE.findMaximallySpecific(cleanCandidates, true); 201 if (maximallySpecificGenericsDiscriminated != null) { 202 return OverloadResolutionResultsImpl.success(maximallySpecificGenericsDiscriminated); 203 } 204 } 205 206 Set<ResolvedCallWithTrace<D>> noOverrides = OverridingUtil.filterOverrides(candidates, MAP_TO_RESULT); 207 if (noOverrides.size() == 1) { 208 return OverloadResolutionResultsImpl.success(noOverrides.iterator().next()); 209 } 210 211 return OverloadResolutionResultsImpl.ambiguity(noOverrides); 212 } 213 214 215}