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.context;
018    
019    import com.intellij.psi.PsiElement;
020    import com.intellij.psi.PsiFile;
021    import kotlin.jvm.functions.Function1;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.psi.KtExpression;
025    import org.jetbrains.kotlin.resolve.BindingTrace;
026    import org.jetbrains.kotlin.resolve.StatementFilter;
027    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
028    import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
029    import org.jetbrains.kotlin.types.KotlinType;
030    import org.jetbrains.kotlin.types.TypeUtils;
031    
032    /**
033     * This class together with its descendants is intended to transfer data flow analysis information
034     * in top-down direction, from AST parents to children.
035     *
036     * NB: all descendants must be immutable!
037     */
038    public abstract class ResolutionContext<Context extends ResolutionContext<Context>> {
039        @NotNull
040        public final BindingTrace trace;
041        @NotNull
042        public final LexicalScope scope;
043        @NotNull
044        public final KotlinType expectedType;
045        @NotNull
046        public final DataFlowInfo dataFlowInfo;
047        @NotNull
048        public final ContextDependency contextDependency;
049        @NotNull
050        public final ResolutionResultsCache resolutionResultsCache;
051        @NotNull
052        public final StatementFilter statementFilter;
053    
054        public final boolean isAnnotationContext;
055    
056        public final boolean isDebuggerContext;
057    
058        public final boolean collectAllCandidates;
059    
060        @NotNull
061        public final CallPosition callPosition;
062    
063        /**
064         * Used for analyzing expression in the given context.
065         * Should be used for going through parents to find containing function, loop etc.
066         * The provider should return specific context expression (which can be used instead of parent)
067         * for the given expression or null otherwise.
068         * @see #getContextParentOfType
069         */
070        @NotNull
071        public final Function1<KtExpression, KtExpression> expressionContextProvider;
072    
073        public static final Function1<KtExpression, KtExpression> DEFAULT_EXPRESSION_CONTEXT_PROVIDER = new Function1<KtExpression, KtExpression>() {
074            @Override
075            public KtExpression invoke(KtExpression expression) {
076                return null;
077            }
078        };
079    
080        protected ResolutionContext(
081                @NotNull BindingTrace trace,
082                @NotNull LexicalScope scope,
083                @NotNull KotlinType expectedType,
084                @NotNull DataFlowInfo dataFlowInfo,
085                @NotNull ContextDependency contextDependency,
086                @NotNull ResolutionResultsCache resolutionResultsCache,
087                @NotNull StatementFilter statementFilter,
088                boolean isAnnotationContext,
089                boolean isDebuggerContext,
090                boolean collectAllCandidates,
091                @NotNull CallPosition callPosition,
092                @NotNull Function1<KtExpression, KtExpression> expressionContextProvider
093        ) {
094            this.trace = trace;
095            this.scope = scope;
096            this.expectedType = expectedType;
097            this.dataFlowInfo = dataFlowInfo;
098            this.contextDependency = contextDependency;
099            this.resolutionResultsCache = resolutionResultsCache;
100            this.statementFilter = statementFilter;
101            this.isAnnotationContext = isAnnotationContext;
102            this.isDebuggerContext = isDebuggerContext;
103            this.collectAllCandidates = collectAllCandidates;
104            this.callPosition = callPosition;
105            this.expressionContextProvider = expressionContextProvider;
106        }
107    
108        protected abstract Context create(
109                @NotNull BindingTrace trace,
110                @NotNull LexicalScope scope,
111                @NotNull DataFlowInfo dataFlowInfo,
112                @NotNull KotlinType expectedType,
113                @NotNull ContextDependency contextDependency,
114                @NotNull ResolutionResultsCache resolutionResultsCache,
115                @NotNull StatementFilter statementFilter,
116                boolean collectAllCandidates,
117                @NotNull CallPosition callPosition,
118                @NotNull Function1<KtExpression, KtExpression> expressionContextProvider
119        );
120    
121        @NotNull
122        private Context self() {
123            //noinspection unchecked
124            return (Context) this;
125        }
126    
127        @NotNull
128        public Context replaceBindingTrace(@NotNull BindingTrace trace) {
129            if (this.trace == trace) return self();
130            return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
131                          collectAllCandidates, callPosition, expressionContextProvider);
132        }
133    
134        @NotNull
135        public Context replaceDataFlowInfo(@NotNull DataFlowInfo newDataFlowInfo) {
136            if (newDataFlowInfo == dataFlowInfo) return self();
137            return create(trace, scope, newDataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
138                          collectAllCandidates, callPosition, expressionContextProvider);
139        }
140    
141        @NotNull
142        public Context replaceExpectedType(@Nullable KotlinType newExpectedType) {
143            if (newExpectedType == null) return replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE);
144            if (expectedType == newExpectedType) return self();
145            return create(trace, scope, dataFlowInfo, newExpectedType, contextDependency, resolutionResultsCache, statementFilter,
146                          collectAllCandidates, callPosition, expressionContextProvider);
147        }
148    
149        @NotNull
150        public Context replaceScope(@NotNull LexicalScope newScope) {
151            if (newScope == scope) return self();
152            return create(trace, newScope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
153                          collectAllCandidates, callPosition, expressionContextProvider);
154        }
155    
156        @NotNull
157        public Context replaceContextDependency(@NotNull ContextDependency newContextDependency) {
158            if (newContextDependency == contextDependency) return self();
159            return create(trace, scope, dataFlowInfo, expectedType, newContextDependency, resolutionResultsCache, statementFilter,
160                          collectAllCandidates, callPosition, expressionContextProvider);
161        }
162    
163        @NotNull
164        public Context replaceResolutionResultsCache(@NotNull ResolutionResultsCache newResolutionResultsCache) {
165            if (newResolutionResultsCache == resolutionResultsCache) return self();
166            return create(trace, scope, dataFlowInfo, expectedType, contextDependency, newResolutionResultsCache, statementFilter,
167                          collectAllCandidates, callPosition, expressionContextProvider);
168        }
169    
170        @NotNull
171        public Context replaceTraceAndCache(@NotNull TemporaryTraceAndCache traceAndCache) {
172            return replaceBindingTrace(traceAndCache.trace).replaceResolutionResultsCache(traceAndCache.cache);
173        }
174    
175        @NotNull
176        public Context replaceCollectAllCandidates(boolean newCollectAllCandidates) {
177            return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
178                          newCollectAllCandidates, callPosition, expressionContextProvider);
179        }
180    
181        @NotNull
182        public Context replaceStatementFilter(@NotNull StatementFilter statementFilter) {
183            return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
184                          collectAllCandidates, callPosition, expressionContextProvider);
185        }
186    
187        @NotNull
188        public Context replaceCallPosition(@NotNull CallPosition callPosition) {
189            return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
190                          collectAllCandidates, callPosition, expressionContextProvider);
191        }
192    
193        @NotNull
194        public Context replaceExpressionContextProvider(@NotNull Function1<KtExpression, KtExpression> expressionContextProvider) {
195            return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
196                          collectAllCandidates, callPosition, expressionContextProvider);
197        }
198    
199        @Nullable
200        public <T extends PsiElement> T getContextParentOfType(@NotNull KtExpression expression, @NotNull Class<? extends T>... classes) {
201            PsiElement current = expression.getParent();
202            while (current != null) {
203                for (Class<? extends T> klass : classes) {
204                    if (klass.isInstance(current)) {
205                        //noinspection unchecked
206                        return (T) current;
207                    }
208                }
209    
210                if (current instanceof PsiFile) return null;
211    
212                if (current instanceof KtExpression) {
213                    KtExpression context = expressionContextProvider.invoke((KtExpression) current);
214                    if (context != null) {
215                        current = context;
216                        continue;
217                    }
218                }
219    
220                current = current.getParent();
221            }
222            return null;
223        }
224    }