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