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
017package org.jetbrains.jet.lang.types.expressions;
018
019import com.google.common.collect.Sets;
020import com.intellij.psi.tree.IElementType;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.descriptors.*;
024import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorUtil;
025import org.jetbrains.jet.lang.diagnostics.Errors;
026import org.jetbrains.jet.lang.psi.*;
027import org.jetbrains.jet.lang.resolve.BindingContext;
028import org.jetbrains.jet.lang.resolve.ModifiersChecker;
029import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
030import org.jetbrains.jet.lang.resolve.TopDownAnalyzer;
031import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
032import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
033import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
034import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsUtil;
035import org.jetbrains.jet.lang.resolve.name.Name;
036import org.jetbrains.jet.lang.resolve.scopes.JetScope;
037import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
038import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
039import org.jetbrains.jet.lang.types.JetType;
040import org.jetbrains.jet.lang.types.JetTypeInfo;
041import org.jetbrains.jet.lang.types.TypeUtils;
042import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
043import org.jetbrains.jet.lexer.JetTokens;
044
045import java.util.Collection;
046
047import static org.jetbrains.jet.lang.diagnostics.Errors.*;
048import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_REFERENCE_TARGET;
049import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE_REASSIGNMENT;
050
051@SuppressWarnings("SuspiciousMethodCalls")
052public 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}