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    
017    package org.jetbrains.jet.lang.types.expressions;
018    
019    import com.google.common.collect.Sets;
020    import com.intellij.psi.tree.IElementType;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.diagnostics.Errors;
025    import org.jetbrains.jet.lang.psi.*;
026    import org.jetbrains.jet.lang.resolve.*;
027    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
028    import org.jetbrains.jet.lang.resolve.calls.context.TemporaryTraceAndCache;
029    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
031    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsUtil;
032    import org.jetbrains.jet.lang.resolve.name.Name;
033    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
035    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
036    import org.jetbrains.jet.lang.types.JetType;
037    import org.jetbrains.jet.lang.types.JetTypeInfo;
038    import org.jetbrains.jet.lang.types.TypeUtils;
039    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
040    import org.jetbrains.jet.lexer.JetTokens;
041    
042    import java.util.Collection;
043    
044    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
045    import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_REFERENCE_TARGET;
046    import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE_REASSIGNMENT;
047    import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
048    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
049    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
050    
051    @SuppressWarnings("SuspiciousMethodCalls")
052    public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisitor {
053        private final WritableScope scope;
054        private final BasicExpressionTypingVisitor basic;
055        private final ControlStructureTypingVisitor controlStructures;
056        private final PatternMatchingTypingVisitor patterns;
057    
058        public ExpressionTypingVisitorForStatements(
059                @NotNull ExpressionTypingInternals facade,
060                @NotNull WritableScope scope,
061                BasicExpressionTypingVisitor basic,
062                @NotNull ControlStructureTypingVisitor controlStructures,
063                @NotNull PatternMatchingTypingVisitor patterns) {
064            super(facade);
065            this.scope = scope;
066            this.basic = basic;
067            this.controlStructures = controlStructures;
068            this.patterns = patterns;
069        }
070    
071        @Nullable
072        private static JetType checkAssignmentType(
073                @Nullable JetType assignmentType,
074                @NotNull JetBinaryExpression expression,
075                @NotNull ExpressionTypingContext context
076        ) {
077            if (assignmentType != null && !KotlinBuiltIns.getInstance().isUnit(assignmentType) && !noExpectedType(context.expectedType) &&
078                TypeUtils.equalTypes(context.expectedType, assignmentType)) {
079                context.trace.report(Errors.ASSIGNMENT_TYPE_MISMATCH.on(expression, context.expectedType));
080                return null;
081            }
082            return DataFlowUtils.checkStatementType(expression, context);
083        }
084    
085        @Override
086        public JetTypeInfo visitObjectDeclaration(@NotNull JetObjectDeclaration declaration, ExpressionTypingContext context) {
087            TopDownAnalyzer.processClassOrObject(
088                    context.replaceScope(scope).replaceContextDependency(INDEPENDENT), scope.getContainingDeclaration(), declaration);
089            ClassDescriptor classDescriptor = context.trace.getBindingContext().get(BindingContext.CLASS, declaration);
090            if (classDescriptor != null) {
091                VariableDescriptor variableDescriptor = context.expressionTypingServices.getDescriptorResolver()
092                        .resolveObjectDeclaration(scope, scope.getContainingDeclaration(), declaration, classDescriptor, context.trace);
093                scope.addVariableDescriptor(variableDescriptor);
094            }
095            return DataFlowUtils.checkStatementType(declaration, context, context.dataFlowInfo);
096        }
097    
098        @Override
099        public JetTypeInfo visitProperty(@NotNull JetProperty property, ExpressionTypingContext typingContext) {
100            ExpressionTypingContext context = typingContext.replaceContextDependency(INDEPENDENT).replaceScope(scope);
101            JetTypeReference receiverTypeRef = property.getReceiverTypeRef();
102            if (receiverTypeRef != null) {
103                context.trace.report(LOCAL_EXTENSION_PROPERTY.on(receiverTypeRef));
104            }
105    
106            JetPropertyAccessor getter = property.getGetter();
107            if (getter != null) {
108                context.trace.report(LOCAL_VARIABLE_WITH_GETTER.on(getter));
109            }
110    
111            JetPropertyAccessor setter = property.getSetter();
112            if (setter != null) {
113                context.trace.report(LOCAL_VARIABLE_WITH_SETTER.on(setter));
114            }
115    
116            JetExpression delegateExpression = property.getDelegateExpression();
117            if (delegateExpression != null) {
118                context.expressionTypingServices.getTypeInfo(delegateExpression, context);
119                context.trace.report(LOCAL_VARIABLE_WITH_DELEGATE.on(property.getDelegate()));
120            }
121    
122            for (JetTypeParameter typeParameter : property.getTypeParameters()) {
123                AnnotationResolver.reportUnsupportedAnnotationForTypeParameter(typeParameter, context.trace);
124            }
125    
126            VariableDescriptor propertyDescriptor = context.expressionTypingServices.getDescriptorResolver().
127                    resolveLocalVariableDescriptor(scope, property, context.dataFlowInfo, context.trace);
128            JetExpression initializer = property.getInitializer();
129            DataFlowInfo dataFlowInfo = context.dataFlowInfo;
130            if (initializer != null) {
131                JetType outType = propertyDescriptor.getType();
132                JetTypeInfo typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType));
133                dataFlowInfo = typeInfo.getDataFlowInfo();
134            }
135    
136            {
137                VariableDescriptor olderVariable = scope.getLocalVariable(propertyDescriptor.getName());
138                ExpressionTypingUtils.checkVariableShadowing(context, propertyDescriptor, olderVariable);
139            }
140    
141            scope.addVariableDescriptor(propertyDescriptor);
142            ModifiersChecker.create(context.trace).checkModifiersForLocalDeclaration(property);
143            return DataFlowUtils.checkStatementType(property, context, dataFlowInfo);
144        }
145    
146        @Override
147        public JetTypeInfo visitMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration, ExpressionTypingContext context) {
148            context.expressionTypingServices.getAnnotationResolver().resolveAnnotationsWithArguments(
149                    scope, multiDeclaration.getModifierList(), context.trace);
150    
151            JetExpression initializer = multiDeclaration.getInitializer();
152            if (initializer == null) {
153                context.trace.report(INITIALIZER_REQUIRED_FOR_MULTIDECLARATION.on(multiDeclaration));
154                return JetTypeInfo.create(null, context.dataFlowInfo);
155            }
156            ExpressionReceiver expressionReceiver = ExpressionTypingUtils.getExpressionReceiver(
157                    facade, initializer, context.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
158            DataFlowInfo dataFlowInfo = facade.getTypeInfo(initializer, context).getDataFlowInfo();
159            if (expressionReceiver == null) {
160                return JetTypeInfo.create(null, dataFlowInfo);
161            }
162            ExpressionTypingUtils.defineLocalVariablesFromMultiDeclaration(scope, multiDeclaration, expressionReceiver, initializer, context);
163            return DataFlowUtils.checkStatementType(multiDeclaration, context, dataFlowInfo);
164        }
165    
166        @Override
167        public JetTypeInfo visitNamedFunction(@NotNull JetNamedFunction function, ExpressionTypingContext context) {
168            SimpleFunctionDescriptor functionDescriptor = context.expressionTypingServices.getDescriptorResolver().
169                    resolveFunctionDescriptorWithAnnotationArguments(
170                            scope.getContainingDeclaration(), scope, function, context.trace, context.dataFlowInfo);
171    
172            scope.addFunctionDescriptor(functionDescriptor);
173            JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(context.scope, functionDescriptor, context.trace);
174            context.expressionTypingServices.checkFunctionReturnType(functionInnerScope, function, functionDescriptor, context.dataFlowInfo, null, context.trace);
175    
176            context.expressionTypingServices.resolveValueParameters(
177                    function.getValueParameters(), functionDescriptor.getValueParameters(), scope, context.dataFlowInfo, context.trace);
178    
179            ModifiersChecker.create(context.trace).checkModifiersForLocalDeclaration(function);
180            return DataFlowUtils.checkStatementType(function, context, context.dataFlowInfo);
181        }
182    
183        @Override
184        public JetTypeInfo visitClass(@NotNull JetClass klass, ExpressionTypingContext context) {
185            TopDownAnalyzer.processClassOrObject(
186                    context.replaceScope(scope).replaceContextDependency(INDEPENDENT), scope.getContainingDeclaration(), klass);
187            ClassDescriptor classDescriptor = context.trace.getBindingContext().get(BindingContext.CLASS, klass);
188            if (classDescriptor != null) {
189                scope.addClassifierDescriptor(classDescriptor);
190            }
191            return DataFlowUtils.checkStatementType(klass, context, context.dataFlowInfo);
192        }
193    
194        @Override
195        public JetTypeInfo visitTypedef(@NotNull JetTypedef typedef, ExpressionTypingContext context) {
196            return super.visitTypedef(typedef, context); // TODO
197        }
198    
199        @Override
200        public JetTypeInfo visitDeclaration(@NotNull JetDeclaration dcl, ExpressionTypingContext context) {
201            return DataFlowUtils.checkStatementType(dcl, context, context.dataFlowInfo);
202        }
203    
204        @Override
205        public JetTypeInfo visitBinaryExpression(@NotNull JetBinaryExpression expression, ExpressionTypingContext context) {
206            JetSimpleNameExpression operationSign = expression.getOperationReference();
207            IElementType operationType = operationSign.getReferencedNameElementType();
208            JetTypeInfo result;
209            if (operationType == JetTokens.EQ) {
210                result = visitAssignment(expression, context);
211            }
212            else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
213                result = visitAssignmentOperation(expression, context);
214            }
215            else {
216                return facade.getTypeInfo(expression, context);
217            }
218            return DataFlowUtils.checkType(result.getType(), expression, context, result.getDataFlowInfo());
219        }
220    
221        @NotNull
222        protected JetTypeInfo visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
223            //There is a temporary binding trace for an opportunity to resolve set method for array if needed (the initial trace should be used there)
224            TemporaryTraceAndCache temporary = TemporaryTraceAndCache.create(
225                    contextWithExpectedType, "trace to resolve array set method for binary expression", expression);
226            ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE)
227                    .replaceTraceAndCache(temporary).replaceContextDependency(INDEPENDENT);
228    
229            JetSimpleNameExpression operationSign = expression.getOperationReference();
230            IElementType operationType = operationSign.getReferencedNameElementType();
231            JetExpression leftOperand = expression.getLeft();
232            JetTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(leftOperand, context, facade);
233            JetType leftType = leftInfo.getType();
234            DataFlowInfo dataFlowInfo = leftInfo.getDataFlowInfo();
235    
236            JetExpression right = expression.getRight();
237            JetExpression left = leftOperand == null ? null : JetPsiUtil.deparenthesize(leftOperand);
238            if (right == null || left == null) {
239                temporary.commit();
240                return JetTypeInfo.create(null, dataFlowInfo);
241            }
242    
243            if (leftType == null) {
244                dataFlowInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo();
245                context.trace.report(UNRESOLVED_REFERENCE.on(operationSign, operationSign));
246                temporary.commit();
247                return JetTypeInfo.create(null, dataFlowInfo);
248            }
249            ExpressionReceiver receiver = new ExpressionReceiver(left, leftType);
250    
251            // We check that defined only one of '+=' and '+' operations, and call it (in the case '+' we then also assign)
252            // Check for '+='
253            Name name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
254            TemporaryTraceAndCache temporaryForAssignmentOperation = TemporaryTraceAndCache.create(
255                    context, "trace to check assignment operation like '+=' for", expression);
256            OverloadResolutionResults<FunctionDescriptor> assignmentOperationDescriptors = BasicExpressionTypingVisitor.getResolutionResultsForBinaryCall(
257                    scope, name, context.replaceTraceAndCache(temporaryForAssignmentOperation), expression, receiver);
258            JetType assignmentOperationType = OverloadResolutionResultsUtil.getResultingType(assignmentOperationDescriptors,
259                                                                                             context.contextDependency);
260    
261            // Check for '+'
262            Name counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
263            TemporaryTraceAndCache temporaryForBinaryOperation = TemporaryTraceAndCache.create(
264                    context, "trace to check binary operation like '+' for", expression);
265            OverloadResolutionResults<FunctionDescriptor> binaryOperationDescriptors = BasicExpressionTypingVisitor.getResolutionResultsForBinaryCall(
266                    scope, counterpartName, context.replaceTraceAndCache(temporaryForBinaryOperation), expression, receiver);
267            JetType binaryOperationType = OverloadResolutionResultsUtil.getResultingType(binaryOperationDescriptors, context.contextDependency);
268    
269            JetType type = assignmentOperationType != null ? assignmentOperationType : binaryOperationType;
270            if (assignmentOperationDescriptors.isSuccess() && binaryOperationDescriptors.isSuccess()) {
271                // Both 'plus()' and 'plusAssign()' available => ambiguity
272                OverloadResolutionResults<FunctionDescriptor> ambiguityResolutionResults = OverloadResolutionResultsUtil.ambiguity(assignmentOperationDescriptors, binaryOperationDescriptors);
273                context.trace.report(ASSIGN_OPERATOR_AMBIGUITY.on(operationSign, ambiguityResolutionResults.getResultingCalls()));
274                Collection<DeclarationDescriptor> descriptors = Sets.newHashSet();
275                for (ResolvedCall<? extends FunctionDescriptor> call : ambiguityResolutionResults.getResultingCalls()) {
276                    descriptors.add(call.getResultingDescriptor());
277                }
278                dataFlowInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo();
279                context.trace.record(AMBIGUOUS_REFERENCE_TARGET, operationSign, descriptors);
280            }
281            else if (assignmentOperationType != null && (assignmentOperationDescriptors.isSuccess() || !binaryOperationDescriptors.isSuccess())) {
282                // There's 'plusAssign()', so we do a.plusAssign(b)
283                temporaryForAssignmentOperation.commit();
284                if (!KotlinBuiltIns.getInstance().isUnit(assignmentOperationType)) {
285                    context.trace.report(ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT.on(operationSign, assignmentOperationDescriptors.getResultingDescriptor(), operationSign));
286                }
287            }
288            else {
289                // There's only 'plus()', so we try 'a = a + b'
290                temporaryForBinaryOperation.commit();
291                context.trace.record(VARIABLE_REASSIGNMENT, expression);
292                if (left instanceof JetArrayAccessExpression) {
293                    ExpressionTypingContext contextForResolve = context.replaceScope(scope).replaceBindingTrace(TemporaryBindingTrace.create(
294                            context.trace, "trace to resolve array set method for assignment", expression));
295                    basic.resolveArrayAccessSetMethod((JetArrayAccessExpression) left, right, contextForResolve, context.trace);
296                }
297                dataFlowInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo();
298                BasicExpressionTypingVisitor.checkLValue(context.trace, leftOperand);
299            }
300            temporary.commit();
301            return JetTypeInfo.create(checkAssignmentType(type, expression, contextWithExpectedType), dataFlowInfo);
302        }
303    
304        @NotNull
305        protected JetTypeInfo visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
306            ExpressionTypingContext context =
307                    contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceScope(scope).replaceContextDependency(INDEPENDENT);
308            JetExpression leftOperand = expression.getLeft();
309            JetExpression left = context.expressionTypingServices.deparenthesizeWithTypeResolution(leftOperand, context);
310            JetExpression right = expression.getRight();
311            if (left instanceof JetArrayAccessExpression) {
312                JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
313                if (right == null) return JetTypeInfo.create(null, context.dataFlowInfo);
314                JetTypeInfo typeInfo = basic.resolveArrayAccessSetMethod(arrayAccessExpression, right, context, context.trace);
315                BasicExpressionTypingVisitor.checkLValue(context.trace, arrayAccessExpression);
316                return JetTypeInfo.create(checkAssignmentType(typeInfo.getType(), expression, contextWithExpectedType),
317                                          typeInfo.getDataFlowInfo());
318            }
319            JetTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade);
320            JetType leftType = leftInfo.getType();
321            DataFlowInfo dataFlowInfo = leftInfo.getDataFlowInfo();
322            if (right != null) {
323                JetTypeInfo rightInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo).replaceExpectedType(leftType));
324                dataFlowInfo = rightInfo.getDataFlowInfo();
325            }
326            if (leftType != null && leftOperand != null) { //if leftType == null, some other error has been generated
327                BasicExpressionTypingVisitor.checkLValue(context.trace, leftOperand);
328            }
329            return DataFlowUtils.checkStatementType(expression, contextWithExpectedType, dataFlowInfo);
330        }
331    
332    
333        @Override
334        public JetTypeInfo visitExpression(@NotNull JetExpression expression, ExpressionTypingContext context) {
335            return facade.getTypeInfo(expression, context);
336        }
337    
338        @Override
339        public JetTypeInfo visitJetElement(@NotNull JetElement element, ExpressionTypingContext context) {
340            context.trace.report(UNSUPPORTED.on(element, "in a block"));
341            return JetTypeInfo.create(null, context.dataFlowInfo);
342        }
343    
344        @Override
345        public JetTypeInfo visitWhileExpression(@NotNull JetWhileExpression expression, ExpressionTypingContext context) {
346            return controlStructures.visitWhileExpression(expression, context, true);
347        }
348    
349        @Override
350        public JetTypeInfo visitDoWhileExpression(@NotNull JetDoWhileExpression expression, ExpressionTypingContext context) {
351            return controlStructures.visitDoWhileExpression(expression, context, true);
352        }
353    
354        @Override
355        public JetTypeInfo visitForExpression(@NotNull JetForExpression expression, ExpressionTypingContext context) {
356            return controlStructures.visitForExpression(expression, context, true);
357        }
358    
359        @Override
360        public JetTypeInfo visitAnnotatedExpression(
361                @NotNull JetAnnotatedExpression expression, ExpressionTypingContext data
362        ) {
363            return basic.visitAnnotatedExpression(expression, data, true);
364        }
365    
366        @Override
367        public JetTypeInfo visitIfExpression(@NotNull JetIfExpression expression, ExpressionTypingContext context) {
368            return controlStructures.visitIfExpression(expression, context, true);
369        }
370    
371        @Override
372        public JetTypeInfo visitWhenExpression(@NotNull JetWhenExpression expression, ExpressionTypingContext context) {
373            return patterns.visitWhenExpression(expression, context, true);
374        }
375    
376        @Override
377        public JetTypeInfo visitBlockExpression(@NotNull JetBlockExpression expression, ExpressionTypingContext context) {
378            return BasicExpressionTypingVisitor.visitBlockExpression(expression, context, true);
379        }
380    
381        @Override
382        public JetTypeInfo visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression, ExpressionTypingContext context) {
383            return basic.visitParenthesizedExpression(expression, context, true);
384        }
385    
386        @Override
387        public JetTypeInfo visitUnaryExpression(@NotNull JetUnaryExpression expression, ExpressionTypingContext context) {
388            return basic.visitUnaryExpression(expression, context, true);
389        }
390    }