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