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