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.types.expressions;
018    
019    import com.intellij.psi.tree.IElementType;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
024    import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
025    import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
026    import org.jetbrains.kotlin.lexer.KtTokens;
027    import org.jetbrains.kotlin.psi.*;
028    import org.jetbrains.kotlin.resolve.*;
029    import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
030    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
031    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
032    import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
033    import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
034    import org.jetbrains.kotlin.types.ErrorUtils;
035    import org.jetbrains.kotlin.types.KotlinType;
036    import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
037    
038    import java.util.Iterator;
039    import java.util.List;
040    
041    import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
042    import static org.jetbrains.kotlin.types.TypeUtils.UNIT_EXPECTED_TYPE;
043    import static org.jetbrains.kotlin.types.expressions.CoercionStrategy.COERCION_TO_UNIT;
044    
045    public class ExpressionTypingServices {
046    
047        private final ExpressionTypingFacade expressionTypingFacade;
048        private final ExpressionTypingComponents expressionTypingComponents;
049    
050        @NotNull private final AnnotationChecker annotationChecker;
051        @NotNull private final StatementFilter statementFilter;
052    
053        public ExpressionTypingServices(
054                @NotNull ExpressionTypingComponents components,
055                @NotNull AnnotationChecker annotationChecker,
056                @NotNull StatementFilter statementFilter,
057                @NotNull ExpressionTypingVisitorDispatcher.ForDeclarations facade
058        ) {
059            this.expressionTypingComponents = components;
060            this.annotationChecker = annotationChecker;
061            this.statementFilter = statementFilter;
062            this.expressionTypingFacade = facade;
063        }
064    
065        @NotNull public StatementFilter getStatementFilter() {
066            return statementFilter;
067        }
068    
069        @NotNull
070        public KotlinType safeGetType(
071                @NotNull LexicalScope scope,
072                @NotNull KtExpression expression,
073                @NotNull KotlinType expectedType,
074                @NotNull DataFlowInfo dataFlowInfo,
075                @NotNull BindingTrace trace
076        ) {
077            KotlinType type = getType(scope, expression, expectedType, dataFlowInfo, trace);
078    
079            return type != null ? type : ErrorUtils.createErrorType("Type for " + expression.getText());
080        }
081    
082        @NotNull
083        public KotlinTypeInfo getTypeInfo(
084                @NotNull LexicalScope scope,
085                @NotNull KtExpression expression,
086                @NotNull KotlinType expectedType,
087                @NotNull DataFlowInfo dataFlowInfo,
088                @NotNull BindingTrace trace,
089                boolean isStatement
090        ) {
091            ExpressionTypingContext context = ExpressionTypingContext.newContext(
092                    trace, scope, dataFlowInfo, expectedType
093            );
094            return expressionTypingFacade.getTypeInfo(expression, context, isStatement);
095        }
096    
097        @NotNull
098        public KotlinTypeInfo getTypeInfo(@NotNull KtExpression expression, @NotNull ResolutionContext resolutionContext) {
099            return expressionTypingFacade.getTypeInfo(expression, ExpressionTypingContext.newContext(resolutionContext));
100        }
101    
102        @Nullable
103        public KotlinType getType(
104                @NotNull LexicalScope scope,
105                @NotNull KtExpression expression,
106                @NotNull KotlinType expectedType,
107                @NotNull DataFlowInfo dataFlowInfo,
108                @NotNull BindingTrace trace
109        ) {
110            return getTypeInfo(scope, expression, expectedType, dataFlowInfo, trace, false).getType();
111        }
112    
113        /////////////////////////////////////////////////////////
114    
115        public void checkFunctionReturnType(
116                @NotNull LexicalScope functionInnerScope,
117                @NotNull KtDeclarationWithBody function,
118                @NotNull FunctionDescriptor functionDescriptor,
119                @NotNull DataFlowInfo dataFlowInfo,
120                @Nullable KotlinType expectedReturnType,
121                BindingTrace trace
122        ) {
123            if (expectedReturnType == null) {
124                expectedReturnType = functionDescriptor.getReturnType();
125                if (!function.hasBlockBody() && !function.hasDeclaredReturnType()) {
126                    expectedReturnType = NO_EXPECTED_TYPE;
127                }
128            }
129            checkFunctionReturnType(function, ExpressionTypingContext.newContext(
130                    trace,
131                    functionInnerScope, dataFlowInfo, expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE
132            ));
133        }
134    
135        /*package*/ void checkFunctionReturnType(KtDeclarationWithBody function, ExpressionTypingContext context) {
136            KtExpression bodyExpression = function.getBodyExpression();
137            if (bodyExpression == null) return;
138    
139            boolean blockBody = function.hasBlockBody();
140            ExpressionTypingContext newContext =
141                    blockBody
142                    ? context.replaceExpectedType(NO_EXPECTED_TYPE)
143                    : context;
144    
145            expressionTypingFacade.getTypeInfo(bodyExpression, newContext, blockBody);
146        }
147    
148        @NotNull
149        public KotlinTypeInfo getBlockReturnedType(KtBlockExpression expression, ExpressionTypingContext context, boolean isStatement) {
150            return getBlockReturnedType(expression, isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION, context);
151        }
152    
153        @NotNull
154        public KotlinTypeInfo getBlockReturnedType(
155                @NotNull KtBlockExpression expression,
156                @NotNull CoercionStrategy coercionStrategyForLastExpression,
157                @NotNull ExpressionTypingContext context
158        ) {
159            List<KtExpression> block = StatementFilterKt.filterStatements(statementFilter, expression);
160    
161            DeclarationDescriptor containingDescriptor = context.scope.getOwnerDescriptor();
162            if (containingDescriptor instanceof ScriptDescriptor) {
163                if (!(expression.getParent() instanceof KtScript)) {
164                    // top level script declarations should have ScriptDescriptor parent
165                    // and lower level script declarations should be ScriptCodeDescriptor parent
166                    containingDescriptor = ((ScriptDescriptor) containingDescriptor).getScriptCodeDescriptor();
167                }
168            }
169            LexicalWritableScope scope = new LexicalWritableScope(context.scope, containingDescriptor, false, null,
170                                                                  new TraceBasedRedeclarationHandler(context.trace), "getBlockReturnedType");
171            scope.changeLockLevel(LexicalWritableScope.LockLevel.BOTH);
172    
173            KotlinTypeInfo r;
174            if (block.isEmpty()) {
175                r = expressionTypingComponents.dataFlowAnalyzer
176                        .createCheckedTypeInfo(expressionTypingComponents.builtIns.getUnitType(), context, expression);
177            }
178            else {
179                r = getBlockReturnedTypeWithWritableScope(scope, block, coercionStrategyForLastExpression,
180                                                          context.replaceStatementFilter(statementFilter));
181            }
182            scope.changeLockLevel(LexicalWritableScope.LockLevel.READING);
183    
184            if (containingDescriptor instanceof ScriptDescriptor) {
185                context.trace.record(BindingContext.SCRIPT_SCOPE, (ScriptDescriptor) containingDescriptor, scope);
186            }
187    
188            return r;
189        }
190    
191        @NotNull
192        public KotlinType getBodyExpressionType(
193                @NotNull BindingTrace trace,
194                @NotNull LexicalScope outerScope,
195                @NotNull DataFlowInfo dataFlowInfo,
196                @NotNull KtDeclarationWithBody function,
197                @NotNull FunctionDescriptor functionDescriptor
198        ) {
199            KtExpression bodyExpression = function.getBodyExpression();
200            assert bodyExpression != null;
201            LexicalScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace);
202    
203            ExpressionTypingContext context = ExpressionTypingContext.newContext(
204                    trace, functionInnerScope, dataFlowInfo, NO_EXPECTED_TYPE
205            );
206            KotlinTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody());
207    
208            KotlinType type = typeInfo.getType();
209            if (type != null) {
210                return type;
211            }
212            else {
213                return ErrorUtils.createErrorType("Error function type");
214            }
215        }
216    
217        /**
218         * Visits block statements propagating data flow information from the first to the last.
219         * Determines block returned type and data flow information at the end of the block AND
220         * at the nearest jump point from the block beginning.
221         */
222        /*package*/ KotlinTypeInfo getBlockReturnedTypeWithWritableScope(
223                @NotNull LexicalWritableScope scope,
224                @NotNull List<? extends KtElement> block,
225                @NotNull CoercionStrategy coercionStrategyForLastExpression,
226                @NotNull ExpressionTypingContext context
227        ) {
228            if (block.isEmpty()) {
229                return TypeInfoFactoryKt.createTypeInfo(expressionTypingComponents.builtIns.getUnitType(), context);
230            }
231    
232            ExpressionTypingInternals blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(
233                    expressionTypingComponents, annotationChecker, scope);
234            ExpressionTypingContext newContext = context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE);
235    
236            KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(context);
237            // Jump point data flow info
238            DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo;
239            boolean jumpOutPossible = false;
240            for (Iterator<? extends KtElement> iterator = block.iterator(); iterator.hasNext(); ) {
241                KtElement statement = iterator.next();
242                if (!(statement instanceof KtExpression)) {
243                    continue;
244                }
245                KtExpression statementExpression = (KtExpression) statement;
246                if (!iterator.hasNext()) {
247                    result = getTypeOfLastExpressionInBlock(
248                            statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression,
249                            blockLevelVisitor);
250                }
251                else {
252                    result = blockLevelVisitor
253                            .getTypeInfo(statementExpression, newContext.replaceContextDependency(ContextDependency.INDEPENDENT), true);
254                }
255    
256                DataFlowInfo newDataFlowInfo = result.getDataFlowInfo();
257                // If jump is not possible, we take new data flow info before jump
258                if (!jumpOutPossible) {
259                    beforeJumpInfo = result.getJumpFlowInfo();
260                    jumpOutPossible = result.getJumpOutPossible();
261                }
262                if (newDataFlowInfo != context.dataFlowInfo) {
263                    newContext = newContext.replaceDataFlowInfo(newDataFlowInfo);
264                    // We take current data flow info if jump there is not possible
265                }
266                blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(expressionTypingComponents, annotationChecker, scope);
267            }
268            return result.replaceJumpOutPossible(jumpOutPossible).replaceJumpFlowInfo(beforeJumpInfo);
269        }
270    
271        private KotlinTypeInfo getTypeOfLastExpressionInBlock(
272                @NotNull KtExpression statementExpression,
273                @NotNull ExpressionTypingContext context,
274                @NotNull CoercionStrategy coercionStrategyForLastExpression,
275                @NotNull ExpressionTypingInternals blockLevelVisitor
276        ) {
277            if (context.expectedType != NO_EXPECTED_TYPE) {
278                KotlinType expectedType;
279                if (context.expectedType == UNIT_EXPECTED_TYPE ||//the first check is necessary to avoid invocation 'isUnit(UNIT_EXPECTED_TYPE)'
280                    (coercionStrategyForLastExpression == COERCION_TO_UNIT && KotlinBuiltIns.isUnit(context.expectedType))) {
281                    expectedType = UNIT_EXPECTED_TYPE;
282                }
283                else {
284                    expectedType = context.expectedType;
285                }
286    
287                return blockLevelVisitor.getTypeInfo(statementExpression, context.replaceExpectedType(expectedType), true);
288            }
289            KotlinTypeInfo result = blockLevelVisitor.getTypeInfo(statementExpression, context, true);
290            if (coercionStrategyForLastExpression == COERCION_TO_UNIT) {
291                boolean mightBeUnit = false;
292                if (statementExpression instanceof KtDeclaration) {
293                    mightBeUnit = true;
294                }
295                if (statementExpression instanceof KtBinaryExpression) {
296                    KtBinaryExpression binaryExpression = (KtBinaryExpression) statementExpression;
297                    IElementType operationType = binaryExpression.getOperationToken();
298                    //noinspection SuspiciousMethodCalls
299                    if (operationType == KtTokens.EQ || OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
300                        mightBeUnit = true;
301                    }
302                }
303                if (mightBeUnit) {
304                    // ExpressionTypingVisitorForStatements should return only null or Unit for declarations and assignments,
305                    // but (for correct assignment / initialization analysis) data flow info must be preserved
306                    assert result.getType() == null || KotlinBuiltIns.isUnit(result.getType());
307                    result = result.replaceType(expressionTypingComponents.builtIns.getUnitType());
308                }
309            }
310            return result;
311        }
312    
313    }