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