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.calls.results;
018    
019    import com.google.common.collect.Sets;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
022    import org.jetbrains.jet.lang.resolve.BindingTrace;
023    import org.jetbrains.jet.lang.resolve.OverridingUtil;
024    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
025    import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy;
026    
027    import java.util.*;
028    
029    import static org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl.MAP_TO_CANDIDATE;
030    import static org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl.MAP_TO_RESULT;
031    import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.*;
032    
033    public 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    }