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