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}