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.Lists;
020 import com.google.common.collect.Maps;
021 import com.intellij.openapi.project.Project;
022 import com.intellij.openapi.util.Pair;
023 import com.intellij.psi.PsiElement;
024 import com.intellij.psi.tree.IElementType;
025 import com.intellij.psi.util.PsiTreeUtil;
026 import org.jetbrains.annotations.NotNull;
027 import org.jetbrains.annotations.Nullable;
028 import org.jetbrains.jet.JetNodeTypes;
029 import org.jetbrains.jet.lang.descriptors.*;
030 import org.jetbrains.jet.lang.diagnostics.Diagnostic;
031 import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
032 import org.jetbrains.jet.lang.diagnostics.Errors;
033 import org.jetbrains.jet.lang.psi.*;
034 import org.jetbrains.jet.lang.resolve.*;
035 import org.jetbrains.jet.lang.resolve.calls.CallResolver;
036 import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
037 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
038 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
039 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
040 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
041 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
042 import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
043 import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
044 import org.jetbrains.jet.lang.resolve.name.FqName;
045 import org.jetbrains.jet.lang.resolve.name.Name;
046 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
047 import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
048 import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
049 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
050 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
051 import org.jetbrains.jet.lang.types.*;
052 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
053 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
054 import org.jetbrains.jet.lexer.JetTokens;
055 import org.jetbrains.jet.util.slicedmap.WritableSlice;
056
057 import java.util.*;
058
059 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
060 import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
061 import static org.jetbrains.jet.lang.resolve.BindingContext.*;
062 import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
063
064 public class ExpressionTypingUtils {
065
066 private final ExpressionTypingServices expressionTypingServices;
067 private final CallResolver callResolver;
068
069 public ExpressionTypingUtils(@NotNull ExpressionTypingServices expressionTypingServices, @NotNull CallResolver resolver) {
070 this.expressionTypingServices = expressionTypingServices;
071 callResolver = resolver;
072 }
073
074 @Nullable
075 protected static ExpressionReceiver getExpressionReceiver(@NotNull JetExpression expression, @Nullable JetType type) {
076 if (type == null) return null;
077 return new ExpressionReceiver(expression, type);
078 }
079
080 @Nullable
081 protected static ExpressionReceiver getExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
082 return getExpressionReceiver(expression, facade.getTypeInfo(expression, context).getType());
083 }
084
085 @NotNull
086 protected static ExpressionReceiver safeGetExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
087 JetType type = safeGetType(facade.safeGetTypeInfo(expression, context));
088 return new ExpressionReceiver(expression, type);
089 }
090
091 @NotNull
092 public static JetType safeGetType(@NotNull JetTypeInfo typeInfo) {
093 JetType type = typeInfo.getType();
094 assert type != null : "safeGetType should be invoked on safe JetTypeInfo; safeGetTypeInfo should return @NotNull type";
095 return type;
096 }
097
098 @NotNull
099 public static WritableScopeImpl newWritableScopeImpl(ExpressionTypingContext context, @NotNull String scopeDebugName) {
100 WritableScopeImpl scope = new WritableScopeImpl(
101 context.scope, context.scope.getContainingDeclaration(), new TraceBasedRedeclarationHandler(context.trace), scopeDebugName);
102 scope.changeLockLevel(WritableScope.LockLevel.BOTH);
103 return scope;
104 }
105
106 public static boolean isBoolean(@NotNull JetType type) {
107 return JetTypeChecker.DEFAULT.isSubtypeOf(type, KotlinBuiltIns.getInstance().getBooleanType());
108 }
109
110 public static boolean ensureBooleanResult(JetExpression operationSign, Name name, JetType resultType, ExpressionTypingContext context) {
111 return ensureBooleanResultWithCustomSubject(operationSign, resultType, "'" + name + "'", context);
112 }
113
114 public static boolean ensureBooleanResultWithCustomSubject(JetExpression operationSign, JetType resultType, String subjectName, ExpressionTypingContext context) {
115 if (resultType != null) {
116 // TODO : Relax?
117 if (!isBoolean(resultType)) {
118 context.trace.report(RESULT_TYPE_MISMATCH.on(operationSign, subjectName, KotlinBuiltIns.getInstance().getBooleanType(), resultType));
119 return false;
120 }
121 }
122 return true;
123 }
124
125 @NotNull
126 public static JetType getDefaultType(IElementType constantType) {
127 if (constantType == JetNodeTypes.INTEGER_CONSTANT) {
128 return KotlinBuiltIns.getInstance().getIntType();
129 }
130 else if (constantType == JetNodeTypes.FLOAT_CONSTANT) {
131 return KotlinBuiltIns.getInstance().getDoubleType();
132 }
133 else if (constantType == JetNodeTypes.BOOLEAN_CONSTANT) {
134 return KotlinBuiltIns.getInstance().getBooleanType();
135 }
136 else if (constantType == JetNodeTypes.CHARACTER_CONSTANT) {
137 return KotlinBuiltIns.getInstance().getCharType();
138 }
139 else if (constantType == JetNodeTypes.NULL) {
140 return KotlinBuiltIns.getInstance().getNullableNothingType();
141 }
142 else {
143 throw new IllegalArgumentException("Unsupported constant type: " + constantType);
144 }
145 }
146
147 private static boolean isCapturedInInline(
148 @NotNull BindingContext context,
149 @NotNull DeclarationDescriptor scopeContainer,
150 @NotNull DeclarationDescriptor variableParent
151 ) {
152 PsiElement scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer);
153 if (!(scopeDeclaration instanceof JetFunctionLiteral)) {
154 return false;
155 }
156
157 PsiElement parent = scopeDeclaration.getParent();
158 assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent;
159 ResolvedCall<?> resolvedCall = CallUtilPackage.getParentResolvedCall((JetFunctionLiteralExpression) parent, context, true);
160 if (resolvedCall == null) {
161 return false;
162 }
163
164 CallableDescriptor callable = resolvedCall.getResultingDescriptor();
165 if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).getInlineStrategy().isInline()) {
166 DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration();
167 assert scopeContainerParent != null : "parent is null for " + scopeContainer;
168 return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent);
169 }
170 else {
171 return false;
172 }
173 }
174
175 public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
176 VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
177 if (variable != null) {
178 DeclarationDescriptor variableParent = variable.getContainingDeclaration();
179 DeclarationDescriptor scopeContainer = scope.getContainingDeclaration();
180 if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) {
181 if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
182 boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent);
183 trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE);
184 }
185 }
186 }
187 }
188
189 /**
190 * Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
191 *
192 * @param callableFQN
193 * @param scope
194 * @return
195 */
196 public static List<CallableDescriptor> canFindSuitableCall(
197 @NotNull FqName callableFQN,
198 @NotNull JetExpression receiverExpression,
199 @NotNull JetType receiverType,
200 @NotNull JetScope scope,
201 @NotNull ModuleDescriptor module
202 ) {
203 JetImportDirective importDirective = JetPsiFactory(receiverExpression).createImportDirective(callableFQN.asString());
204
205 Collection<? extends DeclarationDescriptor> declarationDescriptors = new QualifiedExpressionResolver()
206 .analyseImportReference(importDirective, scope, new BindingTraceContext(), module);
207
208 List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>();
209 ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType);
210
211 for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
212 if (declarationDescriptor instanceof CallableDescriptor) {
213 CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor;
214
215 if (checkIsExtensionCallable(receiverValue, callableDescriptor)) {
216 callableExtensionDescriptors.add(callableDescriptor);
217 }
218 }
219 }
220
221 return callableExtensionDescriptors;
222 }
223
224 /*
225 * Checks if receiver declaration could be resolved to call expected receiver.
226 */
227 public static boolean checkIsExtensionCallable (
228 @NotNull ReceiverValue receiverArgument,
229 @NotNull CallableDescriptor callableDescriptor
230 ) {
231 JetType type = receiverArgument.getType();
232
233 if (type instanceof PackageType) {
234 // This fake class ruins standard algorithms
235 return false;
236 }
237
238 if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
239 if (type.isNullable()) {
240 JetType notNullableType = TypeUtils.makeNotNullable(type);
241 if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
242 }
243 return false;
244 }
245
246 private static boolean checkReceiverResolution (
247 @NotNull ReceiverValue receiverArgument,
248 @NotNull JetType receiverType,
249 @NotNull CallableDescriptor callableDescriptor
250 ) {
251 ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter();
252
253 if (!receiverArgument.exists() && receiverParameter == null) {
254 // Both receivers do not exist
255 return true;
256 }
257
258 if (!(receiverArgument.exists() && receiverParameter != null)) {
259 return false;
260 }
261
262 Set<Name> typeNamesInReceiver = collectUsedTypeNames(receiverParameter.getType());
263
264 ConstraintSystem constraintSystem = new ConstraintSystemImpl();
265 Map<TypeParameterDescriptor, Variance> typeVariables = Maps.newLinkedHashMap();
266 for (TypeParameterDescriptor typeParameterDescriptor : callableDescriptor.getTypeParameters()) {
267 if (typeNamesInReceiver.contains(typeParameterDescriptor.getName())) {
268 typeVariables.put(typeParameterDescriptor, Variance.INVARIANT);
269 }
270 }
271 constraintSystem.registerTypeVariables(typeVariables);
272
273 constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
274 return constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true);
275 }
276
277 private static Set<Name> collectUsedTypeNames(@NotNull JetType jetType) {
278 Set<Name> typeNames = new HashSet<Name>();
279
280 ClassifierDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
281 if (descriptor != null) {
282 typeNames.add(descriptor.getName());
283 }
284
285 for (TypeProjection argument : jetType.getArguments()) {
286 typeNames.addAll(collectUsedTypeNames(argument.getType()));
287 }
288
289 return typeNames;
290 }
291
292 @NotNull
293 public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
294 @NotNull ExpressionTypingContext context,
295 @NotNull ReceiverValue receiver,
296 @NotNull Name name,
297 @NotNull JetType... argumentTypes
298 ) {
299 TemporaryBindingTrace traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for",
300 name);
301 List<JetExpression> fakeArguments = Lists.newArrayList();
302 for (JetType type : argumentTypes) {
303 fakeArguments.add(createFakeExpressionOfType(expressionTypingServices.getProject(), traceWithFakeArgumentInfo,
304 "fakeArgument" + fakeArguments.size(), type));
305 }
306 return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo), fakeArguments, name).getSecond();
307 }
308
309 public static JetExpression createFakeExpressionOfType(
310 @NotNull Project project,
311 @NotNull BindingTrace trace,
312 @NotNull String argumentName,
313 @NotNull JetType argumentType
314 ) {
315 JetExpression fakeExpression = JetPsiFactory(project).createExpression(argumentName);
316 trace.record(EXPRESSION_TYPE, fakeExpression, argumentType);
317 trace.record(PROCESSED, fakeExpression);
318 return fakeExpression;
319 }
320
321 @NotNull
322 public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
323 @NotNull ExpressionTypingContext context,
324 @NotNull ReceiverValue receiver,
325 @NotNull Name name
326 ) {
327 return resolveFakeCall(receiver, context, Collections.<JetExpression>emptyList(), name);
328 }
329
330 @NotNull
331 public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
332 @NotNull ReceiverValue receiver,
333 @NotNull ExpressionTypingContext context,
334 @NotNull List<JetExpression> valueArguments,
335 @NotNull Name name
336 ) {
337 return makeAndResolveFakeCall(receiver, context, valueArguments, name).getSecond();
338 }
339
340 @NotNull
341 public Pair<Call, OverloadResolutionResults<FunctionDescriptor>> makeAndResolveFakeCall(
342 @NotNull ReceiverValue receiver,
343 @NotNull ExpressionTypingContext context,
344 @NotNull List<JetExpression> valueArguments,
345 @NotNull Name name
346 ) {
347 final JetReferenceExpression fake = JetPsiFactory(expressionTypingServices.getProject()).createSimpleName("fake");
348 TemporaryBindingTrace fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name);
349 Call call = CallMaker.makeCallWithExpressions(fake, receiver, null, fake, valueArguments);
350 OverloadResolutionResults<FunctionDescriptor> results =
351 callResolver.resolveCallWithGivenName(context.replaceBindingTrace(fakeTrace), call, fake, name);
352 if (results.isSuccess()) {
353 fakeTrace.commit(new TraceEntryFilter() {
354 @Override
355 public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
356 // excluding all entries related to fake expression
357 return key != fake;
358 }
359 }, true);
360 }
361 return Pair.create(call, results);
362 }
363
364 public void defineLocalVariablesFromMultiDeclaration(
365 @NotNull WritableScope writableScope,
366 @NotNull JetMultiDeclaration multiDeclaration,
367 @NotNull ReceiverValue receiver,
368 @NotNull JetExpression reportErrorsOn,
369 @NotNull ExpressionTypingContext context
370 ) {
371 int componentIndex = 1;
372 for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
373 Name componentName = Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + componentIndex);
374 componentIndex++;
375
376 JetType expectedType = getExpectedTypeForComponent(context, entry);
377 OverloadResolutionResults<FunctionDescriptor> results =
378 resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName);
379
380 JetType componentType = null;
381 if (results.isSuccess()) {
382 context.trace.record(COMPONENT_RESOLVED_CALL, entry, results.getResultingCall());
383 componentType = results.getResultingDescriptor().getReturnType();
384 if (componentType != null && !noExpectedType(expectedType)
385 && !JetTypeChecker.DEFAULT.isSubtypeOf(componentType, expectedType)) {
386
387 context.trace.report(
388 COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.on(reportErrorsOn, componentName, componentType, expectedType));
389 }
390 }
391 else if (results.isAmbiguity()) {
392 context.trace.report(COMPONENT_FUNCTION_AMBIGUITY.on(reportErrorsOn, componentName, results.getResultingCalls()));
393 }
394 else {
395 context.trace.report(COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, componentName, receiver.getType()));
396 }
397 if (componentType == null) {
398 componentType = ErrorUtils.createErrorType(componentName + "() return type");
399 }
400 VariableDescriptor variableDescriptor = expressionTypingServices.getDescriptorResolver().
401 resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace);
402
403 VariableDescriptor olderVariable = writableScope.getLocalVariable(variableDescriptor.getName());
404 checkVariableShadowing(context, variableDescriptor, olderVariable);
405
406 writableScope.addVariableDescriptor(variableDescriptor);
407 }
408 }
409
410 public static void checkVariableShadowing(@NotNull ExpressionTypingContext context, @NotNull VariableDescriptor variableDescriptor, VariableDescriptor oldDescriptor) {
411 if (oldDescriptor != null && isLocal(variableDescriptor.getContainingDeclaration(), oldDescriptor)) {
412 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
413 if (declaration != null) {
414 context.trace.report(Errors.NAME_SHADOWING.on(declaration, variableDescriptor.getName().asString()));
415 }
416 }
417 }
418
419 @NotNull
420 private JetType getExpectedTypeForComponent(ExpressionTypingContext context, JetMultiDeclarationEntry entry) {
421 JetTypeReference entryTypeRef = entry.getTypeRef();
422 if (entryTypeRef != null) {
423 return expressionTypingServices.getTypeResolver().resolveType(context.scope, entryTypeRef, context.trace, true);
424 }
425 else {
426 return TypeUtils.NO_EXPECTED_TYPE;
427 }
428 }
429
430 public static ObservableBindingTrace makeTraceInterceptingTypeMismatch(@NotNull BindingTrace trace, @NotNull final JetElement expressionToWatch, @NotNull final boolean[] mismatchFound) {
431 return new ObservableBindingTrace(trace) {
432
433 @Override
434 public void report(@NotNull Diagnostic diagnostic) {
435 DiagnosticFactory<?> factory = diagnostic.getFactory();
436 if ((factory == TYPE_MISMATCH || factory == CONSTANT_EXPECTED_TYPE_MISMATCH || factory == NULL_FOR_NONNULL_TYPE)
437 && diagnostic.getPsiElement() == expressionToWatch) {
438 mismatchFound[0] = true;
439 }
440 if (TYPE_INFERENCE_ERRORS.contains(factory) &&
441 PsiTreeUtil.isAncestor(expressionToWatch, diagnostic.getPsiElement(), false)) {
442 mismatchFound[0] = true;
443 }
444 super.report(diagnostic);
445 }
446 };
447 }
448
449 @NotNull
450 public static JetTypeInfo getTypeInfoOrNullType(
451 @Nullable JetExpression expression,
452 @NotNull ExpressionTypingContext context,
453 @NotNull ExpressionTypingInternals facade
454 ) {
455 return expression != null
456 ? facade.getTypeInfo(expression, context)
457 : JetTypeInfo.create(null, context.dataFlowInfo);
458 }
459
460 @SuppressWarnings("SuspiciousMethodCalls")
461 public static boolean isBinaryExpressionDependentOnExpectedType(@NotNull JetBinaryExpression expression) {
462 IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
463 return (operationType == JetTokens.IDENTIFIER || OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)
464 || operationType == JetTokens.ELVIS);
465 }
466
467 public static boolean isUnaryExpressionDependentOnExpectedType(@NotNull JetUnaryExpression expression) {
468 return expression.getOperationReference().getReferencedNameElementType() == JetTokens.EXCLEXCL;
469 }
470
471 @NotNull
472 public static List<JetType> getValueParametersTypes(@NotNull List<ValueParameterDescriptor> valueParameters) {
473 List<JetType> parameterTypes = new ArrayList<JetType>(valueParameters.size());
474 for (ValueParameterDescriptor parameter : valueParameters) {
475 parameterTypes.add(parameter.getType());
476 }
477 return parameterTypes;
478 }
479
480 /**
481 * The primary case for local extensions is the following:
482 *
483 * I had a locally declared extension function or a local variable of function type called foo
484 * And I called it on my x
485 * Now, someone added function foo() to the class of x
486 * My code should not change
487 *
488 * thus
489 *
490 * local extension prevail over members (and members prevail over all non-local extensions)
491 */
492 public static boolean isLocal(DeclarationDescriptor containerOfTheCurrentLocality, DeclarationDescriptor candidate) {
493 if (candidate instanceof ValueParameterDescriptor) {
494 return true;
495 }
496 DeclarationDescriptor parent = candidate.getContainingDeclaration();
497 if (!(parent instanceof FunctionDescriptor)) {
498 return false;
499 }
500 FunctionDescriptor functionDescriptor = (FunctionDescriptor) parent;
501 DeclarationDescriptor current = containerOfTheCurrentLocality;
502 while (current != null) {
503 if (current == functionDescriptor) {
504 return true;
505 }
506 current = current.getContainingDeclaration();
507 }
508 return false;
509 }
510
511 public static boolean dependsOnExpectedType(@Nullable JetExpression expression) {
512 JetExpression expr = JetPsiUtil.deparenthesize(expression, false);
513 if (expr == null) return false;
514
515 if (expr instanceof JetBinaryExpressionWithTypeRHS) {
516 return false;
517 }
518 if (expr instanceof JetBinaryExpression) {
519 return isBinaryExpressionDependentOnExpectedType((JetBinaryExpression) expr);
520 }
521 if (expr instanceof JetUnaryExpression) {
522 return isUnaryExpressionDependentOnExpectedType((JetUnaryExpression) expr);
523 }
524 return true;
525 }
526 }