diff --git a/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java index 4b5b9c2c9e8..39b6f7ff34f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java @@ -80,9 +80,9 @@ public class CheckerTestUtil { private static final String SHOULD_BE_ESCAPED = "\\)\\(;"; private static final String DIAGNOSTIC_PARAMETER = "(?:(?:\\\\[" + SHOULD_BE_ESCAPED + "])|[^" + SHOULD_BE_ESCAPED + "])+"; private static final String INDIVIDUAL_DIAGNOSTIC = "(\\w+)(\\(" + DIAGNOSTIC_PARAMETER + "(;\\s*" + DIAGNOSTIC_PARAMETER + ")*\\))?"; - private static final Pattern RANGE_START_OR_END_PATTERN = Pattern.compile("()|()"); + private static final Pattern RANGE_START_OR_END_PATTERN = Pattern.compile("()|()"); private static final Pattern INDIVIDUAL_DIAGNOSTIC_PATTERN = Pattern.compile(INDIVIDUAL_DIAGNOSTIC); private static final Pattern INDIVIDUAL_PARAMETER_PATTERN = Pattern.compile(DIAGNOSTIC_PARAMETER); @@ -98,7 +98,7 @@ public class CheckerTestUtil { new Predicate() { @Override public boolean apply(Diagnostic diagnostic) { - return PsiTreeUtil.isAncestor(root, diagnostic.getPsiElement(), false); + return PsiTreeUtil.isAncestor(root, diagnostic.getPsiElement(), false); } })); for (PsiErrorElement errorElement : AnalyzingUtils.getSyntaxErrorRanges(root)) { @@ -109,6 +109,7 @@ public class CheckerTestUtil { return diagnostics; } + @SuppressWarnings("TestOnlyProblems") @NotNull private static List getDebugInfoDiagnostics( @NotNull PsiElement root, @@ -149,24 +150,29 @@ public class CheckerTestUtil { } }); // this code is used in tests and in internal action 'copy current file as diagnostic test' - //noinspection TestOnlyProblems for (KtExpression expression : bindingContext.getSliceContents(BindingContext.SMARTCAST).keySet()) { if (PsiTreeUtil.isAncestor(root, expression, false)) { debugAnnotations.add(new DebugInfoDiagnostic(expression, DebugInfoDiagnosticFactory.SMARTCAST)); } } - //noinspection TestOnlyProblems for (KtExpression expression : bindingContext.getSliceContents(BindingContext.IMPLICIT_RECEIVER_SMARTCAST).keySet()) { if (PsiTreeUtil.isAncestor(root, expression, false)) { debugAnnotations.add(new DebugInfoDiagnostic(expression, DebugInfoDiagnosticFactory.IMPLICIT_RECEIVER_SMARTCAST)); } } + for (KtExpression expression : bindingContext.getSliceContents(BindingContext.SMARTCAST_NULL).keySet()) { + if (PsiTreeUtil.isAncestor(root, expression, false)) { + debugAnnotations.add(new DebugInfoDiagnostic(expression, DebugInfoDiagnosticFactory.CONSTANT)); + } + } return debugAnnotations; } public interface DiagnosticDiffCallbacks { void missingDiagnostic(TextDiagnostic diagnostic, int expectedStart, int expectedEnd); + void wrongParametersDiagnostic(TextDiagnostic expectedDiagnostic, TextDiagnostic actualDiagnostic, int start, int end); + void unexpectedDiagnostic(TextDiagnostic diagnostic, int actualStart, int actualEnd); } @@ -298,7 +304,8 @@ public class CheckerTestUtil { for (Diagnostic diagnostic : actual) { List textRanges = diagnostic.getTextRanges(); for (TextRange textRange : textRanges) { - callbacks.unexpectedDiagnostic(TextDiagnostic.asTextDiagnostic(diagnostic), textRange.getStartOffset(), textRange.getEndOffset()); + callbacks.unexpectedDiagnostic(TextDiagnostic.asTextDiagnostic(diagnostic), textRange.getStartOffset(), + textRange.getEndOffset()); } } } @@ -343,14 +350,15 @@ public class CheckerTestUtil { matcher.reset(); return matcher.replaceAll(""); } - + public static StringBuffer addDiagnosticMarkersToText(@NotNull PsiFile psiFile, @NotNull Collection diagnostics) { - return addDiagnosticMarkersToText(psiFile, diagnostics, Collections.emptyMap(), new Function() { - @Override - public String fun(PsiFile file) { - return file.getText(); - } - }); + return addDiagnosticMarkersToText(psiFile, diagnostics, Collections.emptyMap(), + new Function() { + @Override + public String fun(PsiFile file) { + return file.getText(); + } + }); } public static StringBuffer addDiagnosticMarkersToText( @@ -397,21 +405,20 @@ public class CheckerTestUtil { } result.append(c); } - + if (currentDescriptor != null) { assert currentDescriptor.start == text.length(); assert currentDescriptor.end == text.length(); openDiagnosticsString(result, currentDescriptor, diagnosticToExpectedDiagnostic); opened.push(currentDescriptor); } - + while (!opened.isEmpty() && text.length() == opened.peek().end) { closeDiagnosticString(result); opened.pop(); } assert opened.isEmpty() : "Stack is not empty: " + opened; - } else { result.append(text); @@ -432,10 +439,12 @@ public class CheckerTestUtil { TextDiagnostic actualTextDiagnostic = TextDiagnostic.asTextDiagnostic(diagnostic); if (compareTextDiagnostic(expectedDiagnostic, actualTextDiagnostic)) { result.append(expectedDiagnostic.asString()); - } else { + } + else { result.append(actualTextDiagnostic.asString()); } - } else { + } + else { result.append(diagnostic.getFactory().getName()); } if (iterator.hasNext()) { @@ -517,12 +526,14 @@ public class CheckerTestUtil { public static class DebugInfoDiagnosticFactory extends DiagnosticFactory { public static final DebugInfoDiagnosticFactory SMARTCAST = new DebugInfoDiagnosticFactory("SMARTCAST"); public static final DebugInfoDiagnosticFactory IMPLICIT_RECEIVER_SMARTCAST = new DebugInfoDiagnosticFactory("IMPLICIT_RECEIVER_SMARTCAST"); + public static final DebugInfoDiagnosticFactory CONSTANT = new DebugInfoDiagnosticFactory("CONSTANT"); public static final DebugInfoDiagnosticFactory ELEMENT_WITH_ERROR_TYPE = new DebugInfoDiagnosticFactory("ELEMENT_WITH_ERROR_TYPE"); public static final DebugInfoDiagnosticFactory UNRESOLVED_WITH_TARGET = new DebugInfoDiagnosticFactory("UNRESOLVED_WITH_TARGET"); public static final DebugInfoDiagnosticFactory MISSING_UNRESOLVED = new DebugInfoDiagnosticFactory("MISSING_UNRESOLVED"); public static final DebugInfoDiagnosticFactory DYNAMIC = new DebugInfoDiagnosticFactory("DYNAMIC"); private final String name; + private DebugInfoDiagnosticFactory(String name, Severity severity) { super(severity); this.name = name; @@ -729,7 +740,7 @@ public class CheckerTestUtil { public void setEnd(int end) { this.end = end; } - + public void addDiagnostic(String diagnostic) { diagnostics.add(TextDiagnostic.parseDiagnostic(diagnostic)); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index db7b5e80741..9ebbde68efb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -131,6 +131,7 @@ public interface BindingContext { WritableSlice> INDEXED_LVALUE_SET = Slices.createSimpleSlice(); WritableSlice SMARTCAST = Slices.createSimpleSlice(); + WritableSlice SMARTCAST_NULL = Slices.createSimpleSlice(); WritableSlice IMPLICIT_RECEIVER_SMARTCAST = Slices.createSimpleSlice(); WritableSlice EXHAUSTIVE_WHEN = Slices.createSimpleSlice(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 523d86e4f85..92b0904afc3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -106,19 +106,55 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return PsiTreeUtil.isAncestor(binaryExpression.getLeft(), expression, false); } + private static boolean isDangerousWithNull(@NotNull KtSimpleNameExpression expression, @NotNull ExpressionTypingContext context) { + PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, KtParenthesizedExpression.class); + if (parent instanceof KtUnaryExpression) { + // Unary: !! only + KtUnaryExpression unaryExpression = (KtUnaryExpression) parent; + return unaryExpression.getOperationToken() == KtTokens.EXCLEXCL; + } + if (parent instanceof KtBinaryExpressionWithTypeRHS) { + // Binary: unsafe as only + KtBinaryExpressionWithTypeRHS binaryExpression = (KtBinaryExpressionWithTypeRHS) parent; + KotlinType type = context.trace.get(TYPE, binaryExpression.getRight()); + return type != null && !type.isMarkedNullable() && + binaryExpression.getOperationReference().getReferencedNameElementType() == KtTokens.AS_KEYWORD; + } + // . is also sensitive, but at receiver only and not for extension functions with nullable receiver + if (parent instanceof KtQualifiedExpression) { + KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) parent; + return qualifiedExpression.getOperationSign() == KtTokens.DOT && + qualifiedExpression.getReceiverExpression() == KtPsiUtil.deparenthesize(expression); + } + return false; + } + + private static void checkNull( + @NotNull KtSimpleNameExpression expression, + @NotNull ExpressionTypingContext context, + @Nullable KotlinType type + ) { + if (type != null && !type.isError() && !isLValue(expression)) { + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, context); + Nullability nullability = context.dataFlowInfo.getPredictableNullability(dataFlowValue); + if (!nullability.canBeNonNull() && nullability.canBeNull()) { + if (isDangerousWithNull(expression, context)) { + context.trace.report(ALWAYS_NULL.on(expression)); + } + else { + context.trace.record(SMARTCAST_NULL, expression); + } + } + } + } + @Override public KotlinTypeInfo visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression, ExpressionTypingContext context) { // TODO : other members // TODO : type substitutions??? CallExpressionResolver callExpressionResolver = components.callExpressionResolver; KotlinTypeInfo typeInfo = callExpressionResolver.getSimpleNameExpressionTypeInfo(expression, NO_RECEIVER, null, context); - if (typeInfo.getType() != null && !typeInfo.getType().isError() && !isLValue(expression)) { - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, typeInfo.getType(), context); - Nullability nullability = context.dataFlowInfo.getPredictableNullability(dataFlowValue); - if (!nullability.canBeNonNull() && nullability.canBeNull()) { - context.trace.report(ALWAYS_NULL.on(expression)); - } - } + checkNull(expression, context, typeInfo.getType()); return components.dataFlowAnalyzer.checkType(typeInfo, expression, context); // TODO : Extensions to this } @@ -144,7 +180,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (!(compileTimeConstant instanceof IntegerValueTypeConstant)) { CompileTimeConstantChecker constantChecker = new CompileTimeConstantChecker(context.trace, components.builtIns, false); - ConstantValue constantValue = compileTimeConstant != null ? ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue() : null; + ConstantValue constantValue = + compileTimeConstant != null ? ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue() : null; boolean hasError = constantChecker.checkConstantExpressionType(constantValue, expression, context.expectedType); if (hasError) { IElementType elementType = expression.getNode().getElementType(); @@ -152,7 +189,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } - assert compileTimeConstant != null : "CompileTimeConstant should be evaluated for constant expression or an error should be recorded " + expression.getText(); + assert compileTimeConstant != null : + "CompileTimeConstant should be evaluated for constant expression or an error should be recorded " + + expression.getText(); return components.dataFlowAnalyzer.createCompileTimeConstantTypeInfo(compileTimeConstant, expression, context); } @@ -180,7 +219,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } @Override - public KotlinTypeInfo visitBinaryWithTypeRHSExpression(@NotNull KtBinaryExpressionWithTypeRHS expression, ExpressionTypingContext context) { + public KotlinTypeInfo visitBinaryWithTypeRHSExpression( + @NotNull KtBinaryExpressionWithTypeRHS expression, + ExpressionTypingContext context + ) { ExpressionTypingContext contextWithNoExpectedType = context.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); KtExpression left = expression.getLeft(); @@ -425,8 +467,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { else { // supertypes may be empty when all the supertypes are error types (are not resolved, for example) KotlinType type = supertypes.isEmpty() - ? components.builtIns.getAnyType() - : supertypes.iterator().next(); + ? components.builtIns.getAnyType() + : supertypes.iterator().next(); result = substitutor.substitute(type, Variance.INVARIANT); } } @@ -437,7 +479,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } context.trace.recordType(expression.getInstanceReference(), result); - context.trace.record(BindingContext.REFERENCE_TARGET, expression.getInstanceReference(), result.getConstructor().getDeclarationDescriptor()); + context.trace.record(BindingContext.REFERENCE_TARGET, expression.getInstanceReference(), + result.getConstructor().getDeclarationDescriptor()); } BindingContextUtilsKt.recordScope(context.trace, context.scope, superTypeQualifier); @@ -506,7 +549,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { trace.record(RESOLVED_CALL, call, resolvedCall); trace.record(CALL, expression, call); - BasicCallResolutionContext resolutionContext = BasicCallResolutionContext.create(context, call, CheckArgumentTypesMode.CHECK_CALLABLE_TYPE); + BasicCallResolutionContext resolutionContext = + BasicCallResolutionContext.create(context, call, CheckArgumentTypesMode.CHECK_CALLABLE_TYPE); resolutionContext.performContextDependentCallChecks(resolvedCall); for (CallChecker checker : components.callCheckers) { checker.check(resolvedCall, resolutionContext); @@ -651,38 +695,47 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } @Override - public KotlinTypeInfo visitObjectLiteralExpression(@NotNull final KtObjectLiteralExpression expression, final ExpressionTypingContext context) { + public KotlinTypeInfo visitObjectLiteralExpression( + @NotNull final KtObjectLiteralExpression expression, + final ExpressionTypingContext context + ) { final KotlinType[] result = new KotlinType[1]; TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve object literal expression", expression); - ObservableBindingTrace.RecordHandler handler = new ObservableBindingTrace.RecordHandler() { + ObservableBindingTrace.RecordHandler handler = + new ObservableBindingTrace.RecordHandler() { - @Override - public void handleRecord(WritableSlice slice, PsiElement declaration, final ClassDescriptor descriptor) { - if (slice == CLASS && declaration == expression.getObjectDeclaration()) { - KotlinType defaultType = DeferredType.createRecursionIntolerant(components.globalContext.getStorageManager(), - context.trace, - new Function0() { - @Override - public KotlinType invoke() { - return descriptor.getDefaultType(); - } - }); - result[0] = defaultType; - } - } - }; + @Override + public void handleRecord( + WritableSlice slice, + PsiElement declaration, + final ClassDescriptor descriptor + ) { + if (slice == CLASS && declaration == expression.getObjectDeclaration()) { + KotlinType defaultType = DeferredType.createRecursionIntolerant(components.globalContext.getStorageManager(), + context.trace, + new Function0() { + @Override + public KotlinType invoke() { + return descriptor.getDefaultType(); + } + }); + result[0] = defaultType; + } + } + }; ObservableBindingTrace traceAdapter = new ObservableBindingTrace(temporaryTrace); traceAdapter.addHandler(CLASS, handler); components.localClassifierAnalyzer.processClassOrObject(null, // don't need to add classifier of object literal to any scope - context.replaceBindingTrace(traceAdapter).replaceContextDependency(INDEPENDENT), + context.replaceBindingTrace(traceAdapter) + .replaceContextDependency(INDEPENDENT), context.scope.getOwnerDescriptor(), expression.getObjectDeclaration()); temporaryTrace.commit(); DataFlowInfo resultFlowInfo = context.dataFlowInfo; - for (KtDelegationSpecifier specifier: expression.getObjectDeclaration().getDelegationSpecifiers()) { + for (KtDelegationSpecifier specifier : expression.getObjectDeclaration().getDelegationSpecifiers()) { if (specifier instanceof KtDelegatorToSuperCall) { - KtDelegatorToSuperCall delegator = (KtDelegatorToSuperCall)specifier; + KtDelegatorToSuperCall delegator = (KtDelegatorToSuperCall) specifier; KotlinTypeInfo delegatorTypeInfo = context.trace.get(EXPRESSION_TYPE_INFO, delegator.getCalleeExpression()); if (delegatorTypeInfo != null) { resultFlowInfo = resultFlowInfo.and(delegatorTypeInfo.getDataFlowInfo()); @@ -749,8 +802,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @Override public KotlinTypeInfo visitUnaryExpression(@NotNull KtUnaryExpression expression, ExpressionTypingContext contextWithExpectedType) { ExpressionTypingContext context = isUnaryExpressionDependentOnExpectedType(expression) - ? contextWithExpectedType - : contextWithExpectedType.replaceContextDependency(INDEPENDENT).replaceExpectedType(NO_EXPECTED_TYPE); + ? contextWithExpectedType + : contextWithExpectedType.replaceContextDependency(INDEPENDENT) + .replaceExpectedType(NO_EXPECTED_TYPE); KtExpression baseExpression = expression.getBaseExpression(); if (baseExpression == null) return TypeInfoFactoryKt.noTypeInfo(context); @@ -779,11 +833,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } // a[i]++/-- takes special treatment because it is actually let j = i, arr = a in arr.set(j, a.get(j).inc()) - if ((operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) && baseExpression instanceof KtArrayAccessExpression) { - KtExpression - stubExpression = ExpressionTypingUtils.createFakeExpressionOfType(baseExpression.getProject(), context.trace, "e", type); - TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace - .create(context.trace, "trace to resolve array access set method for unary expression", expression); + if ((operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) && + baseExpression instanceof KtArrayAccessExpression) { + KtExpression stubExpression = ExpressionTypingUtils.createFakeExpressionOfType( + baseExpression.getProject(), context.trace, "e", type); + TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create( + context.trace, "trace to resolve array access set method for unary expression", expression); ExpressionTypingContext newContext = context.replaceBindingTrace(temporaryBindingTrace); resolveArrayAccessSetMethod((KtArrayAccessExpression) baseExpression, stubExpression, newContext, context.trace); } @@ -812,11 +867,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } else { context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression); - KtExpression stubExpression = ExpressionTypingUtils.createFakeExpressionOfType(baseExpression.getProject(), context.trace, "e", type); + KtExpression stubExpression = ExpressionTypingUtils.createFakeExpressionOfType( + baseExpression.getProject(), context.trace, "e", type); checkLValue(context.trace, context, baseExpression, stubExpression); } // x++ type is x type, but ++x type is x.inc() type - DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) call.getExplicitReceiver(), contextWithExpectedType); + DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue( + (ReceiverValue) call.getExplicitReceiver(), contextWithExpectedType); if (expression instanceof KtPrefixExpression) { result = returnType; } @@ -840,8 +897,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } return components.dataFlowAnalyzer.checkType(typeInfo.replaceType(result), - expression, - contextWithExpectedType.replaceDataFlowInfo(typeInfo.getDataFlowInfo())); + expression, + contextWithExpectedType.replaceDataFlowInfo(typeInfo.getDataFlowInfo())); } private KotlinTypeInfo visitExclExclExpression(@NotNull KtUnaryExpression expression, @NotNull ExpressionTypingContext context) { @@ -852,7 +909,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { // TODO: something must be done for not to lose safe call chain information here // See also CallExpressionResolver.getSimpleNameExpressionTypeInfo, .getQualifiedExpressionTypeInfo - Call call = createCallForSpecialConstruction(expression, expression.getOperationReference(), Collections.singletonList(baseExpression)); + Call call = createCallForSpecialConstruction( + expression, expression.getOperationReference(), Collections.singletonList(baseExpression)); components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( call, ResolveConstruct.EXCL_EXCL, Collections.singletonList("baseExpr"), Collections.singletonList(true), context, null); KotlinTypeInfo baseTypeInfo = BindingContextUtils.getRecordedTypeInfo(baseExpression, context.trace.getBindingContext()); @@ -880,7 +938,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } // The call to checkType() is only needed here to execute additionalTypeCheckers, hence the NO_EXPECTED_TYPE - return components.dataFlowAnalyzer.checkType(baseTypeInfo.replaceType(resultingType), expression, context.replaceExpectedType(NO_EXPECTED_TYPE)); + return components.dataFlowAnalyzer.checkType( + baseTypeInfo.replaceType(resultingType), expression, context.replaceExpectedType(NO_EXPECTED_TYPE)); } @Override @@ -966,7 +1025,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (propertyDescriptor.isSetterProjectedOut()) { trace.report(SETTER_PROJECTED_OUT.on(reportOn, propertyDescriptor)); result = false; - } else { + } + else { if (setter != null) { components.symbolUsageValidator.validateCall(null, setter, trace, reportOn); } @@ -987,8 +1047,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @Override public KotlinTypeInfo visitBinaryExpression(@NotNull KtBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { ExpressionTypingContext context = isBinaryExpressionDependentOnExpectedType(expression) - ? contextWithExpectedType - : contextWithExpectedType.replaceContextDependency(INDEPENDENT).replaceExpectedType(NO_EXPECTED_TYPE); + ? contextWithExpectedType + : contextWithExpectedType.replaceContextDependency(INDEPENDENT) + .replaceExpectedType(NO_EXPECTED_TYPE); KtSimpleNameExpression operationSign = expression.getOperationReference(); KtExpression left = expression.getLeft(); @@ -1110,8 +1171,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (resolutionResults.isSuccess()) { FunctionDescriptor equals = resolutionResults.getResultingCall().getResultingDescriptor(); - if (ensureBooleanResult(operationSign, OperatorNameConventions.EQUALS, equals.getReturnType(), - context)) { + if (ensureBooleanResult(operationSign, OperatorNameConventions.EQUALS, equals.getReturnType(), context)) { ensureNonemptyIntersectionOfOperandTypes(expression, context); } } @@ -1187,7 +1247,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { Call call = createCallForSpecialConstruction(expression, expression.getOperationReference(), Lists.newArrayList(left, right)); ResolvedCall resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( - call, ResolveConstruct.ELVIS, Lists.newArrayList("left", "right"), Lists.newArrayList(true, false), contextWithExpectedType, null); + call, ResolveConstruct.ELVIS, Lists.newArrayList("left", "right"), + Lists.newArrayList(true, false), contextWithExpectedType, null); KotlinTypeInfo leftTypeInfo = BindingContextUtils.getRecordedTypeInfo(left, context.trace.getBindingContext()); if (ArgumentTypeResolver.isFunctionLiteralArgument(left, context)) { context.trace.report(USELESS_ELVIS_ON_FUNCTION_LITERAL.on(expression.getOperationReference())); @@ -1284,7 +1345,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return ensureBooleanResultWithCustomSubject(operationSign, resultType, "'" + name + "'", context); } - private boolean ensureBooleanResultWithCustomSubject(KtExpression operationSign, KotlinType resultType, String subjectName, ExpressionTypingContext context) { + private boolean ensureBooleanResultWithCustomSubject( + KtExpression operationSign, + KotlinType resultType, + String subjectName, + ExpressionTypingContext context + ) { if (resultType != null) { // TODO : Relax? if (!components.builtIns.isBooleanOrSubtype(resultType)) { @@ -1361,7 +1427,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (left != null) { //left here is a receiver, so it doesn't depend on expected type typeInfo = facade.getTypeInfo(left, context.replaceContextDependency(INDEPENDENT).replaceExpectedType(NO_EXPECTED_TYPE)); - } else { + } + else { typeInfo = TypeInfoFactoryKt.noTypeInfo(context); } ExpressionTypingContext contextWithDataFlow = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo()); @@ -1400,8 +1467,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } @Override - public KotlinTypeInfo visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression, ExpressionTypingContext contextWithExpectedType) { - final ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); + public KotlinTypeInfo visitStringTemplateExpression( + @NotNull KtStringTemplateExpression expression, + ExpressionTypingContext contextWithExpectedType + ) { + final ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE) + .replaceContextDependency(INDEPENDENT); checkStringPrefixAndSuffix(expression, context); @@ -1418,7 +1489,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @Override public void visitEscapeStringTemplateEntry(@NotNull KtEscapeStringTemplateEntry entry) { - CompileTimeConstantChecker.CharacterWithDiagnostic value = CompileTimeConstantChecker.escapedStringToCharacter(entry.getText(), entry); + CompileTimeConstantChecker.CharacterWithDiagnostic value = + CompileTimeConstantChecker.escapedStringToCharacter(entry.getText(), entry); Diagnostic diagnostic = value.getDiagnostic(); if (diagnostic != null) { context.trace.report(diagnostic); @@ -1431,8 +1503,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } components.constantExpressionEvaluator.evaluateExpression(expression, context.trace, contextWithExpectedType.expectedType); return components.dataFlowAnalyzer.checkType(visitor.typeInfo.replaceType(components.builtIns.getStringType()), - expression, - contextWithExpectedType); + expression, + contextWithExpectedType); } private static void checkStringPrefixAndSuffix(@NotNull PsiElement expression, ExpressionTypingContext context) { @@ -1478,21 +1550,31 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } @NotNull - /*package*/ KotlinTypeInfo resolveArrayAccessSetMethod(@NotNull KtArrayAccessExpression arrayAccessExpression, @NotNull KtExpression rightHandSide, @NotNull ExpressionTypingContext context, @NotNull BindingTrace traceForResolveResult) { + /*package*/ KotlinTypeInfo resolveArrayAccessSetMethod( + @NotNull KtArrayAccessExpression arrayAccessExpression, + @NotNull KtExpression rightHandSide, + @NotNull ExpressionTypingContext context, + @NotNull BindingTrace traceForResolveResult + ) { return resolveArrayAccessSpecialMethod(arrayAccessExpression, rightHandSide, context, traceForResolveResult, false); } @NotNull - /*package*/ KotlinTypeInfo resolveArrayAccessGetMethod(@NotNull KtArrayAccessExpression arrayAccessExpression, @NotNull ExpressionTypingContext context) { + /*package*/ KotlinTypeInfo resolveArrayAccessGetMethod( + @NotNull KtArrayAccessExpression arrayAccessExpression, + @NotNull ExpressionTypingContext context + ) { return resolveArrayAccessSpecialMethod(arrayAccessExpression, null, context, context.trace, true); } @NotNull - private KotlinTypeInfo resolveArrayAccessSpecialMethod(@NotNull KtArrayAccessExpression arrayAccessExpression, - @Nullable KtExpression rightHandSide, //only for 'set' method - @NotNull ExpressionTypingContext oldContext, - @NotNull BindingTrace traceForResolveResult, - boolean isGet) { + private KotlinTypeInfo resolveArrayAccessSpecialMethod( + @NotNull KtArrayAccessExpression arrayAccessExpression, + @Nullable KtExpression rightHandSide, //only for 'set' method + @NotNull ExpressionTypingContext oldContext, + @NotNull BindingTrace traceForResolveResult, + boolean isGet + ) { KtExpression arrayExpression = arrayAccessExpression.getArrayExpression(); if (arrayExpression == null) return TypeInfoFactoryKt.noTypeInfo(oldContext); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt index 62852b3153b..3e782865f2a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt @@ -50,7 +50,7 @@ object SenselessComparisonChecker { val nullability = getNullability(value) val expressionIsAlways = - if (nullability == Nullability.NULL) return + if (nullability == Nullability.NULL) equality else if (nullability == Nullability.NOT_NULL) !equality else if (nullability == Nullability.IMPOSSIBLE) false else return diff --git a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt index 625b3d7aa61..3588d1586ca 100644 --- a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt +++ b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt @@ -5,24 +5,24 @@ class A() { fun f(): Unit { var x: Int? = 1 x = null - x + 1 - x plus 1 - x < 1 + x + 1 + x plus 1 + x < 1 x += 1 - x == 1 - x != 1 + x == 1 + x != 1 A() == 1 - x === "1" - x !== "1" + x === "1" + x !== "1" - x === 1 - x !== 1 + x === 1 + x !== 1 - x..2 - x in 1..2 + x..2 + x in 1..2 val y : Boolean? = true false || y diff --git a/compiler/testData/diagnostics/tests/Nullability.kt b/compiler/testData/diagnostics/tests/Nullability.kt index 173bad78d23..1edb3cf4d97 100644 --- a/compiler/testData/diagnostics/tests/Nullability.kt +++ b/compiler/testData/diagnostics/tests/Nullability.kt @@ -4,7 +4,7 @@ fun test() { a.plus(1) } else { - a?.plus(1) + a?.plus(1) } val out : java.io.PrintStream? = null @@ -28,7 +28,7 @@ fun test() { } if (out == null) { - out?.println() + out?.println() } else { out.println() } @@ -88,7 +88,7 @@ fun test() { } if (out == null) { - out?.println() + out?.println() } else { out.println() } @@ -140,12 +140,12 @@ fun test() { while (out != null) { out.println(); } - out?.println(); + out?.println(); val out2 : java.io.PrintStream? = null while (out2 == null) { - out2?.println(); + out2?.println(); out2.println(); } out2.println() @@ -235,7 +235,7 @@ fun f7(s : String?, t : String?) { } s?.get(0) if (!(s != null)) { - s?.get(0) + s?.get(0) } else { s.get(0) @@ -245,7 +245,7 @@ fun f7(s : String?, t : String?) { s.get(0) } else { - s?.get(0) + s?.get(0) } s?.get(0) t?.get(0) @@ -264,7 +264,7 @@ fun f7(s : String?, t : String?) { t?.get(0) } else { - s?.get(0) + s?.get(0) t?.get(0) } } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ArrayAccess.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ArrayAccess.kt index a40c8c33504..38c05558895 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ArrayAccess.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ArrayAccess.kt @@ -7,7 +7,7 @@ fun foo() { val x: Int? = null val a = Array(3, {0}) - if (x != null) bar(a[x]) else bar(a[x]) + if (x != null) bar(a[x]) else bar(a[x]) bar(a[if (x == null) 0 else x]) bar(a[x]) diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpression.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpression.kt index c319f861534..a108307440d 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpression.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/BinaryExpression.kt @@ -5,6 +5,6 @@ fun foo() { val x: Int? = null bar(1 + (if (x == null) 0 else x)) - bar(if (x == null) x else x) + bar(if (x == null) x else x) if (x != null) bar(x + x/(x-x*x)) } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt index 0aafb99c7c2..690adabc345 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt @@ -16,15 +16,15 @@ fun foo() { } if (x == null) bar(x) else bar(x) bar(bar(x)) - } else if (x == null) { - bar(x) - if (x != null) { + } else if (x == null) { + bar(x) + if (x != null) { bar(x) if (x == null) bar(x) if (x == null) bar(x) else bar(x) bar(bar(x) + bar(x)) - } else if (x == null) { - bar(x) + } else if (x == null) { + bar(x) } } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DoWhile.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DoWhile.kt index 44ed60af0c5..672242926e3 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DoWhile.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DoWhile.kt @@ -12,5 +12,5 @@ fun foo() { do { bar(y) } while (y != null) - bar(y) + bar(y) } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/IfThenElse.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/IfThenElse.kt index 9a1a0403b94..466c13ee838 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/IfThenElse.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/IfThenElse.kt @@ -6,7 +6,7 @@ fun foo() { bar(if (x == null) 0 else x) if (x == null) { - bar(x) + bar(x) return } else { bar(x) diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ObjectExpression.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ObjectExpression.kt index e0b3cab94f9..d73a059aa74 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ObjectExpression.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ObjectExpression.kt @@ -5,6 +5,6 @@ fun foo() { val a = object { fun baz() = bar(if (x == null) 0 else x) - fun quux(): Int = if (x == null) x else x + fun quux(): Int = if (x == null) x else x } } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Return.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Return.kt index a427ab81e36..e0b97962f01 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Return.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Return.kt @@ -8,10 +8,10 @@ fun foo(): Int { if (x != null) return x val y: Int? = null - if (y == null) return if (y != null) y else y + if (y == null) return if (y != null) y else y val z: Int? = null if (z != null) return if (z == null) z else z - return z + return z } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ThisSuper.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ThisSuper.kt index b758b6a3dcc..02af52d4470 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ThisSuper.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ThisSuper.kt @@ -16,6 +16,6 @@ class Derived : Base() { val y: Int? = null if (y != null) super.bar(this.baz(y)) - else this.baz(super.bar(y)) + else this.baz(super.bar(y)) } } diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Throw.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Throw.kt index b5d0b0051fd..11762476d12 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Throw.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Throw.kt @@ -3,7 +3,7 @@ fun bar(x: Int): RuntimeException = RuntimeException(x.toString()) fun foo() { val x: Int? = null - if (x == null) throw bar(x) + if (x == null) throw bar(x) throw bar(x) throw bar(x) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.kt index 527ad28bc46..945f49932fa 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.kt @@ -3,7 +3,7 @@ fun bar(x: Int): Int = x + 1 fun foo() { val x: Int? = null while (x == null) { - bar(x) + bar(x) } bar(x) @@ -11,11 +11,11 @@ fun foo() { while (y != null) { bar(y) } - bar(y) + bar(y) val z: Int? = null while (z == null) { - bar(z) + bar(z) break } bar(z) diff --git a/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt b/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt index 5fb3ce03e0b..0cb20292f0a 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.kt @@ -6,7 +6,7 @@ interface A fun infer(a: A) : T {} fun test(nothing: Nothing?) { - val i = infer(nothing) + val i = infer(nothing) } fun sum(a : IntArray) : Int { diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/NullableNothingIsExactlyNull.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/NullableNothingIsExactlyNull.kt index f493f14fb93..e669cb2e480 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/NullableNothingIsExactlyNull.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/NullableNothingIsExactlyNull.kt @@ -1,8 +1,8 @@ fun test() { val out : Int? = null val x : Nothing? = null - if (out != x) + if (out != x) out.plus(1) - if (out == x) return + if (out == x) return out.plus(1) } diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2146.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2146.kt index feaf1ca1e37..47964ee6e2c 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2146.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2146.kt @@ -25,7 +25,7 @@ fun f3(s: Int?): Int { fun f4(s: Int?): Int { return when { s == 4 -> s - s == null -> s + s == null -> s else -> s } } diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2164.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2164.kt index 7c22d72818c..d9a8a03c560 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2164.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt2164.kt @@ -21,7 +21,7 @@ fun main(args : Array) { foo(x!!) foo(x) } else { - foo(x) + foo(x) foo(x!!) foo(x) } diff --git a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt index f06cb65d206..30da22dd339 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt @@ -2,7 +2,7 @@ fun test(t: T): String? { if (t != null) { return t?.toString() } - return t?.toString() + return t?.toString() } fun T.testThis(): String? { diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonEquals.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonEquals.kt index 014518122fd..a4272b2c60f 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonEquals.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonEquals.kt @@ -33,10 +33,10 @@ fun test() { if (platformN != null) {} if (platformN == null) {} - if (a == null && platformN == a) {} + if (a == null && platformN == a) {} if (platformJ != null) {} if (platformJ == null) {} - if (a == null && platformJ == a) {} + if (a == null && platformJ == a) {} } diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonIdentityEquals.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonIdentityEquals.kt index f2e0bb3d835..10dc19deefd 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonIdentityEquals.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonIdentityEquals.kt @@ -31,10 +31,10 @@ fun test() { if (platformN !== null) {} if (platformN === null) {} - if (a === null && platformN === a) {} + if (a === null && platformN === a) {} if (platformJ !== null) {} if (platformJ === null) {} - if (a === null && platformJ === a) {} + if (a === null && platformJ === a) {} } diff --git a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt index da6d4ac28eb..08a5244e77b 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt @@ -1,11 +1,23 @@ fun foo(): String { var s: String? s = null - s?.length - if (s == null) s = "z" + s?.length + s.length + if (s == null) return s!! var t: String? = "y" if (t == null) t = "x" var x: Int? = null if (x == null) x += null return t + s +} + +fun String?.gav() {} + +fun bar(s: String?) { + if (s != null) return + // Ideally we should have DEBUG_INFO_CONSTANT instead + s.gav() + s as? String + s as String? + s as String } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.txt b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.txt index 3f9a1d7fdfe..16cf8066fad 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.txt +++ b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.txt @@ -1,3 +1,5 @@ package +public fun bar(/*0*/ s: kotlin.String?): kotlin.Unit public fun foo(): kotlin.String +public fun kotlin.String?.gav(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.kt b/compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.kt new file mode 100644 index 00000000000..ad0ffd954f7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.kt @@ -0,0 +1,16 @@ +// FILE: My.java + +public class My { + static public My create() { return new My(); } + public void foo() {} +} + +// FILE: Test.kt + +fun test() { + val my = My.create() + if (my == null) { + my.foo() + } +} + diff --git a/compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.txt b/compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.txt new file mode 100644 index 00000000000..2fba25084f0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.txt @@ -0,0 +1,14 @@ +package + +public fun test(): kotlin.Unit + +public open class My { + public constructor My() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun create(): My! +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/comparisonUnderAnd.kt b/compiler/testData/diagnostics/tests/smartCasts/comparisonUnderAnd.kt index ac7a34246d1..f712b5356af 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/comparisonUnderAnd.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/comparisonUnderAnd.kt @@ -8,7 +8,7 @@ fun foo(x : String?, y : String?) { x.length y.length } - if (y != null || x == y) { + if (y != null || x == y) { x.length y.length } @@ -17,7 +17,7 @@ fun foo(x : String?, y : String?) { x.length y.length } - if (y == null && x != y) { + if (y == null && x != y) { // y == null but x != y x.length y.length diff --git a/compiler/testData/diagnostics/tests/smartCasts/complexComparison.kt b/compiler/testData/diagnostics/tests/smartCasts/complexComparison.kt index e10a04691ce..76e13dbd283 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/complexComparison.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/complexComparison.kt @@ -3,7 +3,7 @@ fun foo(x: String?, y: String?, z: String?, w: String?) { z.length else z.length - if (x != null || y != null || (x != z && y != z)) + if (x != null || y != null || (x != z && y != z)) z.length else z.length diff --git a/compiler/testData/diagnostics/tests/smartCasts/notNullorNotNull.kt b/compiler/testData/diagnostics/tests/smartCasts/notNullorNotNull.kt index 8d45f94b243..25c361e325e 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/notNullorNotNull.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/notNullorNotNull.kt @@ -1,6 +1,6 @@ fun bar(x: Int?): Int { if (x != null) return -1 - if (x == null) return -2 + if (x == null) return -2 // Should be unreachable return 2 + 2 } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt index a3b90e83445..63c85f364b7 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt @@ -1,5 +1,5 @@ fun foo(): Int { var i: Int? = 42 i = null - return i + 1 + return i + 1 } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/when/ElseOnNullableEnumWithSmartCast.kt b/compiler/testData/diagnostics/tests/when/ElseOnNullableEnumWithSmartCast.kt index 9bd8f3fe69b..0a07c528544 100644 --- a/compiler/testData/diagnostics/tests/when/ElseOnNullableEnumWithSmartCast.kt +++ b/compiler/testData/diagnostics/tests/when/ElseOnNullableEnumWithSmartCast.kt @@ -6,6 +6,6 @@ fun foo(e: E, something: Any?): Int { return when (e) { E.A -> 1 E.B -> 2 - something -> 3 + something -> 3 } } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 2d5bf7d2242..29356ecac0f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -14580,6 +14580,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("alwaysNullWithJava.kt") + public void testAlwaysNullWithJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/alwaysNullWithJava.kt"); + doTest(fileName); + } + @TestMetadata("classObjectMember.kt") public void testClassObjectMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/classObjectMember.kt"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties index 7d1ba16e3f5..adebd0582ec 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties @@ -134,6 +134,7 @@ options.kotlin.attribute.descriptor.constructor.call=Constructor call options.kotlin.attribute.descriptor.variable.as.function.call=Variable as function call options.kotlin.attribute.descriptor.variable.as.function.like.call=Variable as function-like call options.kotlin.attribute.descriptor.smart.cast=Smart-cast value +options.kotlin.attribute.descriptor.smart.constant=Smart constant options.kotlin.attribute.descriptor.smart.cast.receiver=Smart-cast implicit receiver options.kotlin.attribute.descriptor.label=Label change.to.function.invocation=Change to function invocation diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightingColors.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightingColors.java index 6c2d55d88e2..0720295be39 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightingColors.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightingColors.java @@ -83,6 +83,7 @@ public class KotlinHighlightingColors { // other public static final TextAttributesKey BAD_CHARACTER = createTextAttributesKey("KOTLIN_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER); public static final TextAttributesKey SMART_CAST_VALUE = createTextAttributesKey("KOTLIN_SMART_CAST_VALUE"); + public static final TextAttributesKey SMART_CONSTANT = createTextAttributesKey("KOTLIN_SMART_CONSTANT"); public static final TextAttributesKey SMART_CAST_RECEIVER = createTextAttributesKey("KOTLIN_SMART_CAST_RECEIVER"); public static final TextAttributesKey LABEL = createTextAttributesKey("KOTLIN_LABEL"); public static final TextAttributesKey DEBUG_INFO = createTextAttributesKey("KOTLIN_DEBUG_INFO"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/VariablesHighlightingVisitor.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/VariablesHighlightingVisitor.java index f0ad6bd9c1f..0319d07b318 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/VariablesHighlightingVisitor.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/VariablesHighlightingVisitor.java @@ -76,6 +76,12 @@ class VariablesHighlightingVisitor extends AfterAnalysisHighlightingVisitor { .setTextAttributes(KotlinHighlightingColors.SMART_CAST_RECEIVER); } + boolean nullSmartCast = bindingContext.get(SMARTCAST_NULL, expression) == Boolean.TRUE; + if (nullSmartCast) { + holder.createInfoAnnotation(expression, "Always null") + .setTextAttributes(KotlinHighlightingColors.SMART_CONSTANT); + } + KotlinType smartCast = bindingContext.get(SMARTCAST, expression); if (smartCast != null) { holder.createInfoAnnotation(expression, "Smart cast to " + diff --git a/idea/resources/colorScheme/Darcula_Kotlin.xml b/idea/resources/colorScheme/Darcula_Kotlin.xml index 02bd5b202ce..79e46f2670d 100644 --- a/idea/resources/colorScheme/Darcula_Kotlin.xml +++ b/idea/resources/colorScheme/Darcula_Kotlin.xml @@ -30,6 +30,11 @@ + +