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.tasks;
018
019import com.google.common.collect.Lists;
020import com.intellij.openapi.progress.ProgressIndicatorProvider;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.descriptors.*;
024import org.jetbrains.jet.lang.psi.JetExpression;
025import org.jetbrains.jet.lang.psi.JetReferenceExpression;
026import org.jetbrains.jet.lang.psi.JetSuperExpression;
027import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl;
029import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
030import org.jetbrains.jet.lang.resolve.name.Name;
031import org.jetbrains.jet.lang.resolve.scopes.JetScope;
032import org.jetbrains.jet.lang.resolve.scopes.JetScopeUtils;
033import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
034import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
035import org.jetbrains.jet.lang.types.ErrorUtils;
036import org.jetbrains.jet.lang.types.NamespaceType;
037import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
038
039import java.util.Collection;
040import java.util.Collections;
041import java.util.List;
042
043import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
044import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.*;
045import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
046
047public class TaskPrioritizer {
048
049    public static <D extends CallableDescriptor> void splitLexicallyLocalDescriptors(
050            @NotNull Collection<ResolutionCandidate<D>> allDescriptors,
051            @NotNull DeclarationDescriptor containerOfTheCurrentLocality,
052            @NotNull Collection<ResolutionCandidate<D>> local,
053            @NotNull Collection<ResolutionCandidate<D>> nonlocal
054    ) {
055        for (ResolutionCandidate<D> resolvedCall : allDescriptors) {
056            if (DescriptorUtils.isLocal(containerOfTheCurrentLocality, resolvedCall.getDescriptor())) {
057                local.add(resolvedCall);
058            }
059            else {
060                nonlocal.add(resolvedCall);
061            }
062        }
063    }
064
065    @Nullable
066    public static JetSuperExpression getReceiverSuper(@NotNull ReceiverValue receiver) {
067        if (receiver instanceof ExpressionReceiver) {
068            ExpressionReceiver expressionReceiver = (ExpressionReceiver) receiver;
069            JetExpression expression = expressionReceiver.getExpression();
070            if (expression instanceof JetSuperExpression) {
071                return (JetSuperExpression) expression;
072            }
073        }
074        return null;
075    }
076
077    @NotNull
078    public static <D extends CallableDescriptor, F extends D> List<ResolutionTask<D, F>> computePrioritizedTasks(
079            @NotNull BasicCallResolutionContext context,
080            @NotNull Name name,
081            @NotNull JetReferenceExpression functionReference,
082            @NotNull List<CallableDescriptorCollector<? extends D>> callableDescriptorCollectors
083    ) {
084        ReceiverValue explicitReceiver = context.call.getExplicitReceiver();
085        JetScope scope;
086        if (explicitReceiver.exists() && explicitReceiver.getType() instanceof NamespaceType) {
087            // Receiver is a namespace
088            scope = explicitReceiver.getType().getMemberScope();
089            explicitReceiver = NO_RECEIVER;
090        }
091        else {
092            scope = context.scope;
093        }
094
095        ResolutionTaskHolder<D, F> result = new ResolutionTaskHolder<D, F>(functionReference, context, new MyPriorityProvider<D>(context));
096        TaskPrioritizerContext<D, F> c = new TaskPrioritizerContext<D, F>(name, result, context, scope, callableDescriptorCollectors);
097        doComputeTasks(explicitReceiver, c);
098        return result.getTasks();
099    }
100
101    private static <D extends CallableDescriptor, F extends D> void doComputeTasks(
102            @NotNull ReceiverValue receiver,
103            @NotNull TaskPrioritizerContext<D, F> c
104    ) {
105        ProgressIndicatorProvider.checkCanceled();
106
107        boolean resolveInvoke = c.context.call.getThisObject().exists();
108        if (resolveInvoke) {
109            addCandidatesForInvoke(receiver, c);
110            return;
111        }
112        List<ReceiverValue> implicitReceivers = JetScopeUtils.getImplicitReceiversHierarchyValues(c.scope);
113        if (receiver.exists()) {
114            addCandidatesForExplicitReceiver(receiver, implicitReceivers, c, /*resolveInvoke=*/false);
115            return;
116        }
117        addCandidatesForNoReceiver(implicitReceivers, c);
118    }
119
120    private static <D extends CallableDescriptor, F extends D> void addCandidatesForExplicitReceiver(
121            @NotNull ReceiverValue receiver,
122            @NotNull List<ReceiverValue> implicitReceivers,
123            @NotNull TaskPrioritizerContext<D, F> c,
124            boolean resolveInvoke
125    ) {
126
127        List<ReceiverValue> variantsForExplicitReceiver = c.autoCastService.getVariantsForReceiver(receiver);
128
129        //members
130        for (CallableDescriptorCollector<? extends D> callableDescriptorCollector : c.callableDescriptorCollectors) {
131            Collection<ResolutionCandidate<D>> members = Lists.newArrayList();
132            for (ReceiverValue variant : variantsForExplicitReceiver) {
133                Collection<? extends D> membersForThisVariant = callableDescriptorCollector.getMembersByName(variant.getType(), c.name);
134                convertWithReceivers(membersForThisVariant, Collections.singletonList(variant),
135                                     Collections.singletonList(NO_RECEIVER), members, resolveInvoke);
136            }
137            c.result.addCandidates(members);
138        }
139
140        for (CallableDescriptorCollector<? extends D> callableDescriptorCollector : c.callableDescriptorCollectors) {
141            //member extensions
142            for (ReceiverValue implicitReceiver : implicitReceivers) {
143                addMemberExtensionCandidates(implicitReceiver, variantsForExplicitReceiver,
144                                             callableDescriptorCollector, c, resolveInvoke);
145            }
146            //extensions
147            Collection<ResolutionCandidate<D>> extensionFunctions = convertWithImpliedThis(
148                    c.scope, variantsForExplicitReceiver, callableDescriptorCollector.getNonMembersByName(c.scope, c.name));
149            c.result.addCandidates(extensionFunctions);
150        }
151    }
152
153    private static <D extends CallableDescriptor, F extends D> void addMemberExtensionCandidates(
154            @NotNull ReceiverValue implicitReceiver,
155            @NotNull List<ReceiverValue> variantsForExplicitReceiver,
156            @NotNull CallableDescriptorCollector<? extends D> callableDescriptorCollector, TaskPrioritizerContext<D, F> c,
157            boolean resolveInvoke
158    ) {
159        Collection<? extends D> memberExtensions = callableDescriptorCollector.getNonMembersByName(
160                implicitReceiver.getType().getMemberScope(), c.name);
161        List<ReceiverValue> variantsForImplicitReceiver = c.autoCastService.getVariantsForReceiver(implicitReceiver);
162        c.result.addCandidates(convertWithReceivers(memberExtensions, variantsForImplicitReceiver,
163                                                  variantsForExplicitReceiver, resolveInvoke));
164    }
165
166    private static <D extends CallableDescriptor, F extends D> void addCandidatesForNoReceiver(
167            @NotNull List<ReceiverValue> implicitReceivers,
168            @NotNull TaskPrioritizerContext<D, F> c
169    ) {
170        List<Collection<ResolutionCandidate<D>>> localsList = Lists.newArrayList();
171        List<Collection<ResolutionCandidate<D>>> nonlocalsList = Lists.newArrayList();
172        for (CallableDescriptorCollector<? extends D> callableDescriptorCollector : c.callableDescriptorCollectors) {
173
174            Collection<ResolutionCandidate<D>> functions =
175                    convertWithImpliedThis(c.scope, Collections.singletonList(NO_RECEIVER), callableDescriptorCollector
176                            .getNonExtensionsByName(c.scope, c.name));
177
178            List<ResolutionCandidate<D>> nonlocals = Lists.newArrayList();
179            List<ResolutionCandidate<D>> locals = Lists.newArrayList();
180            //noinspection unchecked,RedundantTypeArguments
181            TaskPrioritizer.<D>splitLexicallyLocalDescriptors(functions, c.scope.getContainingDeclaration(), locals, nonlocals);
182
183            localsList.add(locals);
184            nonlocalsList.add(nonlocals);
185        }
186
187        //locals
188        c.result.addCandidates(localsList);
189
190        //try all implicit receivers as explicit
191        for (ReceiverValue implicitReceiver : implicitReceivers) {
192            addCandidatesForExplicitReceiver(implicitReceiver, implicitReceivers, c, /*resolveInvoke=*/false);
193        }
194        
195        //nonlocals
196        c.result.addCandidates(nonlocalsList);
197    }
198
199    private static <D extends CallableDescriptor, F extends D> void addCandidatesForInvoke(
200            @NotNull ReceiverValue explicitReceiver,
201            @NotNull TaskPrioritizerContext<D, F> c
202    ) {
203        List<ReceiverValue> implicitReceivers = JetScopeUtils.getImplicitReceiversHierarchyValues(c.scope);
204
205        // For 'a.foo()' where foo has function type,
206        // a is explicitReceiver, foo is variableReceiver.
207        ReceiverValue variableReceiver = c.context.call.getThisObject();
208        assert variableReceiver.exists() : "'Invoke' call hasn't got variable receiver";
209
210        // For invocation a.foo() explicit receiver 'a'
211        // can be a receiver for 'foo' variable
212        // or for 'invoke' function.
213
214        // (1) a.foo + foo.invoke()
215        if (!explicitReceiver.exists()) {
216            addCandidatesForExplicitReceiver(variableReceiver, implicitReceivers, c, /*resolveInvoke=*/true);
217        }
218
219        // (2) foo + a.invoke()
220
221        // 'invoke' is member extension to explicit receiver while variable receiver is 'this object'
222        //trait A
223        //trait Foo { fun A.invoke() }
224
225        if (explicitReceiver.exists()) {
226            //a.foo()
227            addCandidatesWhenInvokeIsMemberExtensionToExplicitReceiver(variableReceiver, explicitReceiver, c);
228            return;
229        }
230        // with (a) { foo() }
231        for (ReceiverValue implicitReceiver : implicitReceivers) {
232            addCandidatesWhenInvokeIsMemberExtensionToExplicitReceiver(variableReceiver, implicitReceiver, c);
233        }
234    }
235
236    private static <D extends CallableDescriptor, F extends D> void addCandidatesWhenInvokeIsMemberExtensionToExplicitReceiver(
237            @NotNull ReceiverValue variableReceiver,
238            @NotNull ReceiverValue explicitReceiver,
239            @NotNull TaskPrioritizerContext<D, F> c
240    ) {
241        List<ReceiverValue> variantsForExplicitReceiver = c.autoCastService.getVariantsForReceiver(explicitReceiver);
242
243        for (CallableDescriptorCollector<? extends D> callableDescriptorCollector : c.callableDescriptorCollectors) {
244            addMemberExtensionCandidates(variableReceiver, variantsForExplicitReceiver, callableDescriptorCollector, c, /*resolveInvoke=*/true);
245        }
246    }
247
248    private static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithReceivers(
249            @NotNull Collection<? extends D> descriptors,
250            @NotNull Iterable<ReceiverValue> thisObjects,
251            @NotNull Iterable<ReceiverValue> receiverParameters,
252            boolean hasExplicitThisObject
253    ) {
254        Collection<ResolutionCandidate<D>> result = Lists.newArrayList();
255        convertWithReceivers(descriptors, thisObjects, receiverParameters, result, hasExplicitThisObject);
256        return result;
257    }
258
259    private static <D extends CallableDescriptor> void convertWithReceivers(
260            @NotNull Collection<? extends D> descriptors,
261            @NotNull Iterable<ReceiverValue> thisObjects,
262            @NotNull Iterable<ReceiverValue> receiverParameters,
263            @NotNull Collection<ResolutionCandidate<D>> result,
264            boolean hasExplicitThisObject
265    ) {
266        for (ReceiverValue thisObject : thisObjects) {
267            for (ReceiverValue receiverParameter : receiverParameters) {
268                for (D extension : descriptors) {
269                    if (DescriptorUtils.isConstructorOfStaticNestedClass(extension)) {
270                        // We don't want static nested classes' constructors to be resolved with expectedThisObject
271                        continue;
272                    }
273                    ResolutionCandidate<D> candidate = ResolutionCandidate.create(extension);
274                    candidate.setThisObject(thisObject);
275                    candidate.setReceiverArgument(receiverParameter);
276                    candidate.setExplicitReceiverKind(
277                            hasExplicitThisObject ? ExplicitReceiverKind.BOTH_RECEIVERS : ExplicitReceiverKind.THIS_OBJECT);
278                    result.add(candidate);
279                }
280            }
281        }
282    }
283
284    public static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithImpliedThis(
285            @NotNull JetScope scope,
286            @NotNull Collection<ReceiverValue> receiverParameters,
287            @NotNull Collection<? extends D> descriptors
288    ) {
289        Collection<ResolutionCandidate<D>> result = Lists.newArrayList();
290        for (ReceiverValue receiverParameter : receiverParameters) {
291            for (D descriptor : descriptors) {
292                ResolutionCandidate<D> candidate = ResolutionCandidate.create(descriptor);
293                candidate.setReceiverArgument(receiverParameter);
294                candidate.setExplicitReceiverKind(
295                        receiverParameter.exists() ? ExplicitReceiverKind.RECEIVER_ARGUMENT : ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
296                if (setImpliedThis(scope, candidate)) {
297                    result.add(candidate);
298                }
299            }
300        }
301        if (receiverParameters.size() == 1 && !receiverParameters.iterator().next().exists()) {
302            for (D descriptor : descriptors) {
303                if (descriptor.getExpectedThisObject() != null && descriptor.getReceiverParameter() == null) {
304                    DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
305                    if (descriptor instanceof ConstructorDescriptor) {
306                        containingDeclaration = containingDeclaration.getContainingDeclaration();
307                    }
308                    if (containingDeclaration != null && isClassObject(containingDeclaration)) {
309                        ResolutionCandidate<D> candidate = ResolutionCandidate.create(descriptor);
310                        candidate.setThisObject(((ClassDescriptor) containingDeclaration).getThisAsReceiverParameter().getValue());
311                        candidate.setExplicitReceiverKind(ExplicitReceiverKind.NO_EXPLICIT_RECEIVER);
312                        result.add(candidate);
313                    }
314                }
315            }
316        }
317        return result;
318    }
319
320    private static <D extends CallableDescriptor> boolean setImpliedThis(
321            @NotNull JetScope scope,
322            @NotNull ResolutionCandidate<D> candidate
323    ) {
324        ReceiverParameterDescriptor expectedThisObject = candidate.getDescriptor().getExpectedThisObject();
325        if (expectedThisObject == null) return true;
326        List<ReceiverParameterDescriptor> receivers = scope.getImplicitReceiversHierarchy();
327        for (ReceiverParameterDescriptor receiver : receivers) {
328            if (JetTypeChecker.INSTANCE.isSubtypeOf(receiver.getType(), expectedThisObject.getType())) {
329                // TODO : Autocasts & nullability
330                candidate.setThisObject(expectedThisObject.getValue());
331                return true;
332            }
333        }
334        return false;
335    }
336
337    public static <D extends CallableDescriptor, F extends D> List<ResolutionTask<D, F>> computePrioritizedTasksFromCandidates(
338            @NotNull BasicCallResolutionContext context,
339            @NotNull JetReferenceExpression functionReference,
340            @NotNull Collection<ResolutionCandidate<D>> candidates
341    ) {
342        ResolutionTaskHolder<D, F> result = new ResolutionTaskHolder<D, F>(functionReference, context, new MyPriorityProvider<D>(context));
343        result.addCandidates(candidates);
344        return result.getTasks();
345    }
346
347    private static class MyPriorityProvider<D extends CallableDescriptor>
348            implements ResolutionTaskHolder.PriorityProvider<ResolutionCandidate<D>> {
349        private final BasicCallResolutionContext context;
350
351        public MyPriorityProvider(BasicCallResolutionContext context) {
352            this.context = context;
353        }
354
355        @Override
356        public int getPriority(ResolutionCandidate<D> call) {
357            return (isVisible(call) ? 2 : 0) + (isSynthesized(call) ? 0 : 1);
358        }
359
360        @Override
361        public int getMaxPriority() {
362            return 3;
363        }
364
365        private boolean isVisible(ResolutionCandidate<D> call) {
366            if (call == null) return false;
367            D candidateDescriptor = call.getDescriptor();
368            if (ErrorUtils.isError(candidateDescriptor)) return true;
369            return Visibilities.isVisible(candidateDescriptor, context.scope.getContainingDeclaration());
370        }
371
372        private boolean isSynthesized(ResolutionCandidate<D> call) {
373            D descriptor = call.getDescriptor();
374            return descriptor instanceof CallableMemberDescriptor &&
375                   isOrOverridesSynthesized((CallableMemberDescriptor) descriptor);
376        }
377    }
378
379    private static class TaskPrioritizerContext<D extends CallableDescriptor, F extends D> {
380        @NotNull public final Name name;
381        @NotNull public final ResolutionTaskHolder<D, F> result;
382        @NotNull public final BasicCallResolutionContext context;
383        @NotNull public final JetScope scope;
384        @NotNull public final List<CallableDescriptorCollector<? extends D>> callableDescriptorCollectors;
385        @NotNull AutoCastServiceImpl autoCastService;
386
387        private TaskPrioritizerContext(
388                @NotNull Name name,
389                @NotNull ResolutionTaskHolder<D, F> result,
390                @NotNull BasicCallResolutionContext context,
391                @NotNull JetScope scope,
392                @NotNull List<CallableDescriptorCollector<? extends D>> callableDescriptorCollectors
393        ) {
394            this.name = name;
395            this.result = result;
396            this.context = context;
397            this.scope = scope;
398            this.callableDescriptorCollectors = callableDescriptorCollectors;
399            autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.trace.getBindingContext());
400        }
401    }
402}