Limit "always null" scope: only for !!, is and dot; senseless comparison rolled back; "smart constant" information for nulls #KT-10029 Fixed
This commit is contained in:
@@ -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("(<!"+
|
||||
INDIVIDUAL_DIAGNOSTIC +"(,\\s*"+
|
||||
INDIVIDUAL_DIAGNOSTIC +")*!>)|(<!>)");
|
||||
private static final Pattern RANGE_START_OR_END_PATTERN = Pattern.compile("(<!" +
|
||||
INDIVIDUAL_DIAGNOSTIC + "(,\\s*" +
|
||||
INDIVIDUAL_DIAGNOSTIC + ")*!>)|(<!>)");
|
||||
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<Diagnostic>() {
|
||||
@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<Diagnostic> 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<TextRange> 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<Diagnostic> diagnostics) {
|
||||
return addDiagnosticMarkersToText(psiFile, diagnostics, Collections.<Diagnostic, TextDiagnostic>emptyMap(), new Function<PsiFile, String>() {
|
||||
@Override
|
||||
public String fun(PsiFile file) {
|
||||
return file.getText();
|
||||
}
|
||||
});
|
||||
return addDiagnosticMarkersToText(psiFile, diagnostics, Collections.<Diagnostic, TextDiagnostic>emptyMap(),
|
||||
new Function<PsiFile, String>() {
|
||||
@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<DebugInfoDiagnostic> {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ public interface BindingContext {
|
||||
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_SET = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice<KtExpression, KotlinType> SMARTCAST = Slices.createSimpleSlice();
|
||||
WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice();
|
||||
WritableSlice<KtExpression, KotlinType> IMPLICIT_RECEIVER_SMARTCAST = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice<KtWhenExpression, Boolean> EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
||||
|
||||
+149
-67
@@ -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<PsiElement, ClassDescriptor> handler = new ObservableBindingTrace.RecordHandler<PsiElement, ClassDescriptor>() {
|
||||
ObservableBindingTrace.RecordHandler<PsiElement, ClassDescriptor> handler =
|
||||
new ObservableBindingTrace.RecordHandler<PsiElement, ClassDescriptor>() {
|
||||
|
||||
@Override
|
||||
public void handleRecord(WritableSlice<PsiElement, ClassDescriptor> slice, PsiElement declaration, final ClassDescriptor descriptor) {
|
||||
if (slice == CLASS && declaration == expression.getObjectDeclaration()) {
|
||||
KotlinType defaultType = DeferredType.createRecursionIntolerant(components.globalContext.getStorageManager(),
|
||||
context.trace,
|
||||
new Function0<KotlinType>() {
|
||||
@Override
|
||||
public KotlinType invoke() {
|
||||
return descriptor.getDefaultType();
|
||||
}
|
||||
});
|
||||
result[0] = defaultType;
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void handleRecord(
|
||||
WritableSlice<PsiElement, ClassDescriptor> slice,
|
||||
PsiElement declaration,
|
||||
final ClassDescriptor descriptor
|
||||
) {
|
||||
if (slice == CLASS && declaration == expression.getObjectDeclaration()) {
|
||||
KotlinType defaultType = DeferredType.createRecursionIntolerant(components.globalContext.getStorageManager(),
|
||||
context.trace,
|
||||
new Function0<KotlinType>() {
|
||||
@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<FunctionDescriptor> 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);
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -5,24 +5,24 @@ class A() {
|
||||
fun f(): Unit {
|
||||
var x: Int? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>1<!>
|
||||
x = null
|
||||
<!ALWAYS_NULL!>x<!> <!UNSAFE_INFIX_CALL!>+<!> 1
|
||||
<!ALWAYS_NULL!>x<!> <!UNSAFE_INFIX_CALL, INFIX_MODIFIER_REQUIRED!>plus<!> 1
|
||||
<!ALWAYS_NULL!>x<!> <!UNSAFE_INFIX_CALL!><<!> 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> <!UNSAFE_INFIX_CALL!>+<!> 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> <!UNSAFE_INFIX_CALL, INFIX_MODIFIER_REQUIRED!>plus<!> 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> <!UNSAFE_INFIX_CALL!><<!> 1
|
||||
x <!UNSAFE_INFIX_CALL!>+=<!> 1
|
||||
|
||||
<!ALWAYS_NULL!>x<!> == 1
|
||||
<!ALWAYS_NULL!>x<!> != 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> == 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> != 1
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE!>A() == 1<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE!><!ALWAYS_NULL!>x<!> === "1"<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!><!ALWAYS_NULL!>x<!> !== "1"<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!><!DEBUG_INFO_CONSTANT!>x<!> === "1"<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!><!DEBUG_INFO_CONSTANT!>x<!> !== "1"<!>
|
||||
|
||||
<!ALWAYS_NULL!>x<!> === 1
|
||||
<!ALWAYS_NULL!>x<!> !== 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> === 1
|
||||
<!DEBUG_INFO_CONSTANT!>x<!> !== 1
|
||||
|
||||
<!ALWAYS_NULL!>x<!><!UNSAFE_INFIX_CALL!>..<!>2
|
||||
<!ALWAYS_NULL, TYPE_MISMATCH!>x<!> in 1..2
|
||||
<!DEBUG_INFO_CONSTANT!>x<!><!UNSAFE_INFIX_CALL!>..<!>2
|
||||
<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!> in 1..2
|
||||
|
||||
val y : Boolean? = true
|
||||
<!UNUSED_EXPRESSION!>false || <!TYPE_MISMATCH!>y<!><!>
|
||||
|
||||
+8
-8
@@ -4,7 +4,7 @@ fun test() {
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.plus(1)
|
||||
}
|
||||
else {
|
||||
<!ALWAYS_NULL!>a<!>?.plus(1)
|
||||
<!DEBUG_INFO_CONSTANT!>a<!>?.plus(1)
|
||||
}
|
||||
|
||||
val out : java.io.PrintStream? = null
|
||||
@@ -28,7 +28,7 @@ fun test() {
|
||||
}
|
||||
|
||||
if (out == null) {
|
||||
<!ALWAYS_NULL!>out<!>?.println()
|
||||
<!DEBUG_INFO_CONSTANT!>out<!>?.println()
|
||||
} else {
|
||||
<!DEBUG_INFO_SMARTCAST!>out<!>.println()
|
||||
}
|
||||
@@ -88,7 +88,7 @@ fun test() {
|
||||
}
|
||||
|
||||
if (out == null) {
|
||||
<!ALWAYS_NULL!>out<!>?.println()
|
||||
<!DEBUG_INFO_CONSTANT!>out<!>?.println()
|
||||
} else {
|
||||
<!DEBUG_INFO_SMARTCAST!>out<!>.println()
|
||||
}
|
||||
@@ -140,12 +140,12 @@ fun test() {
|
||||
while (out != null) {
|
||||
<!DEBUG_INFO_SMARTCAST!>out<!>.println();
|
||||
}
|
||||
<!ALWAYS_NULL!>out<!>?.println();
|
||||
<!DEBUG_INFO_CONSTANT!>out<!>?.println();
|
||||
|
||||
val out2 : java.io.PrintStream? = null
|
||||
|
||||
while (out2 == null) {
|
||||
<!ALWAYS_NULL!>out2<!>?.println();
|
||||
<!DEBUG_INFO_CONSTANT!>out2<!>?.println();
|
||||
<!ALWAYS_NULL!>out2<!><!UNSAFE_CALL!>.<!>println();
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>out2<!>.println()
|
||||
@@ -235,7 +235,7 @@ fun f7(s : String?, t : String?) {
|
||||
}
|
||||
s?.get(0)
|
||||
if (!(s != null)) {
|
||||
<!ALWAYS_NULL!>s<!>?.get(0)
|
||||
<!DEBUG_INFO_CONSTANT!>s<!>?.get(0)
|
||||
}
|
||||
else {
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.get(0)
|
||||
@@ -245,7 +245,7 @@ fun f7(s : String?, t : String?) {
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.get(0)
|
||||
}
|
||||
else {
|
||||
<!ALWAYS_NULL!>s<!>?.get(0)
|
||||
<!DEBUG_INFO_CONSTANT!>s<!>?.get(0)
|
||||
}
|
||||
s?.get(0)
|
||||
t?.get(0)
|
||||
@@ -264,7 +264,7 @@ fun f7(s : String?, t : String?) {
|
||||
t?.get(0)
|
||||
}
|
||||
else {
|
||||
<!ALWAYS_NULL!>s<!>?.get(0)
|
||||
<!DEBUG_INFO_CONSTANT!>s<!>?.get(0)
|
||||
t?.get(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ fun foo() {
|
||||
val x: Int? = null
|
||||
val a = Array<Int>(3, {0})
|
||||
|
||||
if (x != null) bar(a[<!DEBUG_INFO_SMARTCAST!>x<!>]) else bar(a[<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>])
|
||||
if (x != null) bar(a[<!DEBUG_INFO_SMARTCAST!>x<!>]) else bar(a[<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>])
|
||||
bar(a[if (x == null) 0 else <!DEBUG_INFO_SMARTCAST!>x<!>])
|
||||
bar(a[<!TYPE_MISMATCH!>x<!>])
|
||||
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@ fun foo() {
|
||||
val x: Int? = null
|
||||
|
||||
bar(1 + (if (x == null) 0 else x))
|
||||
bar(if (x == null) <!ALWAYS_NULL, TYPE_MISMATCH!>x<!> else x)
|
||||
bar(if (x == null) <!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!> else x)
|
||||
if (x != null) bar(x + x/(x-x*x))
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ fun foo() {
|
||||
}
|
||||
if (<!SENSELESS_COMPARISON!>x == null<!>) bar(x) else bar(x)
|
||||
bar(bar(x))
|
||||
} else if (<!ALWAYS_NULL!>x<!> == null) {
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>)
|
||||
if (<!ALWAYS_NULL!>x<!> != null) {
|
||||
} else if (<!SENSELESS_COMPARISON!><!DEBUG_INFO_CONSTANT!>x<!> == null<!>) {
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>)
|
||||
if (<!SENSELESS_COMPARISON!><!DEBUG_INFO_CONSTANT!>x<!> != null<!>) {
|
||||
bar(x)
|
||||
if (<!SENSELESS_COMPARISON!>x == null<!>) bar(x)
|
||||
if (<!SENSELESS_COMPARISON!>x == null<!>) bar(x) else bar(x)
|
||||
bar(bar(x) + bar(x))
|
||||
} else if (<!ALWAYS_NULL!>x<!> == null) {
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>)
|
||||
} else if (<!SENSELESS_COMPARISON!><!DEBUG_INFO_CONSTANT!>x<!> == null<!>) {
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,5 +12,5 @@ fun foo() {
|
||||
do {
|
||||
bar(<!TYPE_MISMATCH!>y<!>)
|
||||
} while (y != null)
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>y<!>)
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>y<!>)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ fun foo() {
|
||||
bar(if (x == null) 0 else <!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
|
||||
if (x == null) {
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>)
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>)
|
||||
return
|
||||
} else {
|
||||
bar(<!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@ fun foo() {
|
||||
|
||||
val <!UNUSED_VARIABLE!>a<!> = object {
|
||||
fun baz() = bar(if (x == null) 0 else <!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
fun quux(): Int = if (x == null) <!ALWAYS_NULL, TYPE_MISMATCH!>x<!> else <!DEBUG_INFO_SMARTCAST!>x<!>
|
||||
fun quux(): Int = if (x == null) <!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!> else <!DEBUG_INFO_SMARTCAST!>x<!>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ fun foo(): Int {
|
||||
if (x != null) return x
|
||||
|
||||
val y: Int? = null
|
||||
if (y == null) return if (<!ALWAYS_NULL!>y<!> != null) y else <!ALWAYS_NULL, TYPE_MISMATCH!>y<!>
|
||||
if (y == null) return if (<!SENSELESS_COMPARISON!><!DEBUG_INFO_CONSTANT!>y<!> != null<!>) y else <!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>y<!>
|
||||
|
||||
val z: Int? = null
|
||||
if (z != null) return if (<!SENSELESS_COMPARISON!>z == null<!>) z else z
|
||||
|
||||
return <!ALWAYS_NULL, TYPE_MISMATCH!>z<!>
|
||||
return <!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>z<!>
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ class Derived : Base() {
|
||||
|
||||
val y: Int? = null
|
||||
if (y != null) super.bar(this.baz(<!DEBUG_INFO_SMARTCAST!>y<!>))
|
||||
else this.baz(super.bar(<!ALWAYS_NULL, TYPE_MISMATCH!>y<!>))
|
||||
else this.baz(super.bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>y<!>))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ fun bar(x: Int): RuntimeException = RuntimeException(x.toString())
|
||||
fun foo() {
|
||||
val x: Int? = null
|
||||
|
||||
if (x == null) throw bar(<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>)
|
||||
if (x == null) throw bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>)
|
||||
throw bar(<!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
<!UNREACHABLE_CODE!>throw bar(<!DEBUG_INFO_SMARTCAST!>x<!>)<!>
|
||||
}
|
||||
@@ -3,7 +3,7 @@ fun bar(x: Int): Int = x + 1
|
||||
fun foo() {
|
||||
val x: Int? = null
|
||||
while (x == null) {
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>)
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>)
|
||||
}
|
||||
bar(<!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
|
||||
@@ -11,11 +11,11 @@ fun foo() {
|
||||
while (y != null) {
|
||||
bar(<!DEBUG_INFO_SMARTCAST!>y<!>)
|
||||
}
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>y<!>)
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>y<!>)
|
||||
|
||||
val z: Int? = null
|
||||
while (z == null) {
|
||||
bar(<!ALWAYS_NULL, TYPE_MISMATCH!>z<!>)
|
||||
bar(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>z<!>)
|
||||
break
|
||||
}
|
||||
bar(<!TYPE_MISMATCH!>z<!>)
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ interface A<T>
|
||||
fun <T> infer(<!UNUSED_PARAMETER!>a<!>: A<T>) : T {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
|
||||
|
||||
fun test(nothing: Nothing?) {
|
||||
val <!UNUSED_VARIABLE!>i<!> = <!TYPE_INFERENCE_INCORPORATION_ERROR!>infer<!>(<!ALWAYS_NULL, TYPE_MISMATCH!>nothing<!>)
|
||||
val <!UNUSED_VARIABLE!>i<!> = <!TYPE_INFERENCE_INCORPORATION_ERROR!>infer<!>(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>nothing<!>)
|
||||
}
|
||||
|
||||
fun sum(<!UNUSED_PARAMETER!>a<!> : IntArray) : Int {
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
fun test() {
|
||||
val out : Int? = null
|
||||
val x : Nothing? = null
|
||||
if (out != <!ALWAYS_NULL!>x<!>)
|
||||
if (out != <!DEBUG_INFO_CONSTANT!>x<!>)
|
||||
<!DEBUG_INFO_SMARTCAST!>out<!>.plus(1)
|
||||
if (out == <!ALWAYS_NULL!>x<!>) return
|
||||
if (out == <!DEBUG_INFO_CONSTANT!>x<!>) return
|
||||
<!DEBUG_INFO_SMARTCAST!>out<!>.plus(1)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ fun f3(s: Int?): Int {
|
||||
fun f4(s: Int?): Int {
|
||||
return when {
|
||||
s == 4 -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||
s == null -> <!ALWAYS_NULL, TYPE_MISMATCH!>s<!>
|
||||
s == null -> <!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>s<!>
|
||||
else -> <!DEBUG_INFO_SMARTCAST!>s<!>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ fun main(args : Array<String>) {
|
||||
foo(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
|
||||
foo(<!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
} else {
|
||||
foo(<!ALWAYS_NULL, TYPE_MISMATCH!>x<!>)
|
||||
foo(<!TYPE_MISMATCH, DEBUG_INFO_CONSTANT!>x<!>)
|
||||
<!UNREACHABLE_CODE!>foo(<!><!ALWAYS_NULL!>x<!>!!<!UNREACHABLE_CODE!>)<!>
|
||||
<!UNREACHABLE_CODE!>foo(<!DEBUG_INFO_SMARTCAST!>x<!>)<!>
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ fun <T> test(t: T): String? {
|
||||
if (t != null) {
|
||||
return t<!UNNECESSARY_SAFE_CALL!>?.<!>toString()
|
||||
}
|
||||
return <!ALWAYS_NULL!>t<!>?.toString()
|
||||
return <!DEBUG_INFO_CONSTANT!>t<!>?.toString()
|
||||
}
|
||||
|
||||
fun <T> T.testThis(): String? {
|
||||
|
||||
Vendored
+2
-2
@@ -33,10 +33,10 @@ fun test() {
|
||||
|
||||
if (platformN != null) {}
|
||||
if (platformN == null) {}
|
||||
if (a == null && platformN == <!ALWAYS_NULL!>a<!>) {}
|
||||
if (a == null && platformN == <!DEBUG_INFO_CONSTANT!>a<!>) {}
|
||||
|
||||
if (platformJ != null) {}
|
||||
if (platformJ == null) {}
|
||||
if (a == null && platformJ == <!ALWAYS_NULL!>a<!>) {}
|
||||
if (a == null && platformJ == <!DEBUG_INFO_CONSTANT!>a<!>) {}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -31,10 +31,10 @@ fun test() {
|
||||
|
||||
if (platformN !== null) {}
|
||||
if (platformN === null) {}
|
||||
if (a === null && platformN === <!ALWAYS_NULL!>a<!>) {}
|
||||
if (a === null && platformN === <!DEBUG_INFO_CONSTANT!>a<!>) {}
|
||||
|
||||
if (platformJ !== null) {}
|
||||
if (platformJ === null) {}
|
||||
if (a === null && platformJ === <!ALWAYS_NULL!>a<!>) {}
|
||||
if (a === null && platformJ === <!DEBUG_INFO_CONSTANT!>a<!>) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
fun foo(): String {
|
||||
var s: String?
|
||||
s = null
|
||||
<!ALWAYS_NULL!>s<!>?.length
|
||||
if (<!ALWAYS_NULL!>s<!> == null) s = "z"
|
||||
<!DEBUG_INFO_CONSTANT!>s<!>?.length
|
||||
<!ALWAYS_NULL!>s<!><!UNSAFE_CALL!>.<!>length
|
||||
if (<!SENSELESS_COMPARISON!><!DEBUG_INFO_CONSTANT!>s<!> == null<!>) return <!ALWAYS_NULL!>s<!>!!
|
||||
var t: String? = "y"
|
||||
if (t == null) t = "x"
|
||||
var x: Int? = null
|
||||
if (x == null) <!TYPE_MISMATCH!>x += null<!>
|
||||
return <!DEBUG_INFO_SMARTCAST!>t<!> + s
|
||||
}
|
||||
|
||||
fun String?.gav() {}
|
||||
|
||||
fun bar(s: String?) {
|
||||
if (s != null) return
|
||||
// Ideally we should have DEBUG_INFO_CONSTANT instead
|
||||
<!ALWAYS_NULL!>s<!>.gav()
|
||||
<!DEBUG_INFO_CONSTANT!>s<!> as? String
|
||||
<!DEBUG_INFO_CONSTANT!>s<!> <!USELESS_CAST!>as String?<!>
|
||||
<!ALWAYS_NULL!>s<!> as String
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
<!ALWAYS_NULL!>my<!>.foo()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
}
|
||||
@@ -8,7 +8,7 @@ fun foo(x : String?, y : String?) {
|
||||
x<!UNSAFE_CALL!>.<!>length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
if (y != null || x == <!ALWAYS_NULL!>y<!>) {
|
||||
if (y != null || x == <!DEBUG_INFO_CONSTANT!>y<!>) {
|
||||
x<!UNSAFE_CALL!>.<!>length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
@@ -17,7 +17,7 @@ fun foo(x : String?, y : String?) {
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
<!ALWAYS_NULL!>y<!><!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
if (y == null && x != <!ALWAYS_NULL!>y<!>) {
|
||||
if (y == null && x != <!DEBUG_INFO_CONSTANT!>y<!>) {
|
||||
// y == null but x != y
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
<!ALWAYS_NULL!>y<!><!UNSAFE_CALL!>.<!>length
|
||||
|
||||
@@ -3,7 +3,7 @@ fun foo(x: String?, y: String?, z: String?, w: String?) {
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length
|
||||
else
|
||||
z<!UNSAFE_CALL!>.<!>length
|
||||
if (x != null || y != null || (<!ALWAYS_NULL!>x<!> != z && <!ALWAYS_NULL!>y<!> != z))
|
||||
if (x != null || y != null || (<!DEBUG_INFO_CONSTANT!>x<!> != z && <!DEBUG_INFO_CONSTANT!>y<!> != z))
|
||||
z<!UNSAFE_CALL!>.<!>length
|
||||
else
|
||||
<!ALWAYS_NULL!>z<!><!UNSAFE_CALL!>.<!>length
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
fun bar(x: Int?): Int {
|
||||
if (x != null) return -1
|
||||
if (<!ALWAYS_NULL!>x<!> == null) return -2
|
||||
if (<!SENSELESS_COMPARISON!><!DEBUG_INFO_CONSTANT!>x<!> == null<!>) return -2
|
||||
// Should be unreachable
|
||||
return 2 + 2
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fun foo(): Int {
|
||||
var i: Int? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>42<!>
|
||||
i = null
|
||||
return <!ALWAYS_NULL!>i<!> <!UNSAFE_INFIX_CALL!>+<!> 1
|
||||
return <!DEBUG_INFO_CONSTANT!>i<!> <!UNSAFE_INFIX_CALL!>+<!> 1
|
||||
}
|
||||
+1
-1
@@ -6,6 +6,6 @@ fun foo(e: E, something: Any?): Int {
|
||||
return when (e) {
|
||||
E.A -> 1
|
||||
E.B -> 2
|
||||
<!ALWAYS_NULL!>something<!> -> 3
|
||||
<!DEBUG_INFO_CONSTANT!>something<!> -> 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
@@ -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");
|
||||
|
||||
+6
@@ -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 " +
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
<option name="BACKGROUND" value="223c23" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="KOTLIN_SMART_CONSTANT">
|
||||
<value>
|
||||
<option name="BACKGROUND" value="223c23" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="KOTLIN_SMART_CAST_RECEIVER">
|
||||
<value>
|
||||
<option name="BACKGROUND" value="223c23" />
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
<option name="BACKGROUND" value="dbffdb" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="KOTLIN_SMART_CONSTANT">
|
||||
<value>
|
||||
<option name="BACKGROUND" value="dbffdb" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="KOTLIN_SMART_CAST_RECEIVER">
|
||||
<value>
|
||||
<option name="BACKGROUND" value="dbffdb" />
|
||||
|
||||
@@ -67,6 +67,9 @@ fun Int?.bar() {
|
||||
if (this != null) {
|
||||
println(<SMART_CAST_RECEIVER>toString</SMART_CAST_RECEIVER>())
|
||||
}
|
||||
else {
|
||||
println(<SMART_CONSTANT>this</SMART_CONSTANT>.toString())
|
||||
}
|
||||
}
|
||||
|
||||
var <PROPERTY_WITH_BACKING_FIELD><PACKAGE_PROPERTY><MUTABLE_VARIABLE>globalCounter</MUTABLE_VARIABLE></PACKAGE_PROPERTY></PROPERTY_WITH_BACKING_FIELD> : Int = 5
|
||||
@@ -152,6 +155,7 @@ var <PROPERTY_WITH_BACKING_FIELD><PACKAGE_PROPERTY><MUTABLE_VARIABLE>globalCount
|
||||
KotlinBundle.message("options.kotlin.attribute.descriptor.variable.as.function.like.call") to KotlinHighlightingColors.VARIABLE_AS_FUNCTION_LIKE_CALL,
|
||||
OptionsBundle.message("options.java.attribute.descriptor.bad.character") to KotlinHighlightingColors.BAD_CHARACTER,
|
||||
KotlinBundle.message("options.kotlin.attribute.descriptor.smart.cast") to KotlinHighlightingColors.SMART_CAST_VALUE,
|
||||
KotlinBundle.message("options.kotlin.attribute.descriptor.smart.constant") to KotlinHighlightingColors.SMART_CONSTANT,
|
||||
KotlinBundle.message("options.kotlin.attribute.descriptor.smart.cast.receiver") to KotlinHighlightingColors.SMART_CAST_RECEIVER,
|
||||
KotlinBundle.message("options.kotlin.attribute.descriptor.label") to KotlinHighlightingColors.LABEL)
|
||||
}
|
||||
|
||||
Vendored
+8
-8
@@ -4,7 +4,7 @@ fun test() {
|
||||
a.plus(1)
|
||||
}
|
||||
else {
|
||||
<warning>a</warning>?.plus(1)
|
||||
a?.plus(1)
|
||||
}
|
||||
|
||||
val out : java.io.PrintStream? = null
|
||||
@@ -28,7 +28,7 @@ fun test() {
|
||||
}
|
||||
|
||||
if (out == null) {
|
||||
<warning>out</warning>?.println()
|
||||
out?.println()
|
||||
} else {
|
||||
out.println()
|
||||
}
|
||||
@@ -84,7 +84,7 @@ fun test() {
|
||||
}
|
||||
|
||||
if (out == null) {
|
||||
<warning>out</warning>?.println()
|
||||
out?.println()
|
||||
} else {
|
||||
out.println()
|
||||
}
|
||||
@@ -131,12 +131,12 @@ fun test() {
|
||||
while (out != null) {
|
||||
out.println();
|
||||
}
|
||||
<warning>out</warning>?.println();
|
||||
out?.println();
|
||||
|
||||
val out2 : java.io.PrintStream? = null
|
||||
|
||||
while (out2 == null) {
|
||||
<warning>out2</warning>?.println();
|
||||
out2?.println();
|
||||
}
|
||||
out2.println()
|
||||
|
||||
@@ -224,7 +224,7 @@ fun f7(s : String?, t : String?) {
|
||||
}
|
||||
s?.get(0)
|
||||
if (!(s != null)) {
|
||||
<warning>s</warning>?.get(0)
|
||||
s?.get(0)
|
||||
}
|
||||
else {
|
||||
s.get(0)
|
||||
@@ -234,7 +234,7 @@ fun f7(s : String?, t : String?) {
|
||||
s.get(0)
|
||||
}
|
||||
else {
|
||||
<warning>s</warning>?.get(0)
|
||||
s?.get(0)
|
||||
}
|
||||
s?.get(0)
|
||||
t?.get(0)
|
||||
@@ -253,7 +253,7 @@ fun f7(s : String?, t : String?) {
|
||||
t?.get(0)
|
||||
}
|
||||
else {
|
||||
<warning>s</warning>?.get(0)
|
||||
s?.get(0)
|
||||
t?.get(0)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -1,6 +1,12 @@
|
||||
class <info textAttributesKey="KOTLIN_CLASS">My</info>(val <info textAttributesKey="KOTLIN_INSTANCE_PROPERTY"><info textAttributesKey="KOTLIN_PARAMETER"><info textAttributesKey="KOTLIN_PROPERTY_WITH_BACKING_FIELD">x</info></info></info>: <info textAttributesKey="KOTLIN_CLASS">Int</info>?)
|
||||
|
||||
fun <info textAttributesKey="KOTLIN_CLASS">My</info>?.<info textAttributesKey="KOTLIN_FUNCTION_DECLARATION">foo</info>(): <info textAttributesKey="KOTLIN_CLASS">Int</info> {
|
||||
if (this == null || <info textAttributesKey="KOTLIN_INSTANCE_PROPERTY"><info textAttributesKey="KOTLIN_SMART_CAST_RECEIVER">x</info></info> == null) return 42
|
||||
if (this == null) return 42
|
||||
if (<info textAttributesKey="KOTLIN_INSTANCE_PROPERTY"><info textAttributesKey="KOTLIN_SMART_CAST_RECEIVER">x</info></info> == null) {
|
||||
if (<warning textAttributesKey="WARNING_ATTRIBUTES"><info textAttributesKey="KOTLIN_INSTANCE_PROPERTY"><info textAttributesKey="KOTLIN_SMART_CONSTANT"><info textAttributesKey="KOTLIN_SMART_CAST_RECEIVER">x</info></info></info> != null</warning>) {
|
||||
<warning textAttributesKey="WARNING_ATTRIBUTES">return</warning> <info textAttributesKey="KOTLIN_INSTANCE_PROPERTY"><info textAttributesKey="KOTLIN_SMART_CAST_RECEIVER"><info textAttributesKey="KOTLIN_SMART_CAST_VALUE">x</info></info></info>
|
||||
}
|
||||
return 13
|
||||
}
|
||||
return <info textAttributesKey="KOTLIN_INSTANCE_PROPERTY"><info textAttributesKey="KOTLIN_SMART_CAST_RECEIVER"><info textAttributesKey="KOTLIN_SMART_CAST_VALUE">x</info></info></info>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user