mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-11 00:21:31 +00:00
Compare commits
112 Commits
abannykh/j
...
beta5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
994eceabc0 | ||
|
|
e46544cc5e | ||
|
|
c0668334b4 | ||
|
|
1ad2c6e4a7 | ||
|
|
26b3c06963 | ||
|
|
362f1641b8 | ||
|
|
889126cc25 | ||
|
|
000b14d86e | ||
|
|
258ae08a6e | ||
|
|
a0d9271e00 | ||
|
|
3ceba38a78 | ||
|
|
c28e9b0f3c | ||
|
|
5b23a86f6e | ||
|
|
8c1cd3a1ed | ||
|
|
ab7c2cb82a | ||
|
|
b707da415c | ||
|
|
ddfdff0b30 | ||
|
|
e7c81388b7 | ||
|
|
b00d05ecad | ||
|
|
f5ead3507a | ||
|
|
a4b9c32933 | ||
|
|
8839e9f707 | ||
|
|
4f9b441a66 | ||
|
|
e4116624c3 | ||
|
|
4dd91c0a81 | ||
|
|
47ad2370e3 | ||
|
|
39598530d5 | ||
|
|
fa98553461 | ||
|
|
94369a24d5 | ||
|
|
036828b923 | ||
|
|
5f27032bae | ||
|
|
cee7724d9f | ||
|
|
a86fcf1141 | ||
|
|
02262534db | ||
|
|
2706dd1d8e | ||
|
|
2b1b54bc28 | ||
|
|
0cd0f54b21 | ||
|
|
24522175e5 | ||
|
|
74d7d109b9 | ||
|
|
fe5df28123 | ||
|
|
62f92f2453 | ||
|
|
839c1d29d3 | ||
|
|
ecddf5ee20 | ||
|
|
b7b099e795 | ||
|
|
292055276d | ||
|
|
90491cf683 | ||
|
|
d4c55159bd | ||
|
|
b7451c6e2b | ||
|
|
3941ac308d | ||
|
|
28f7ed010b | ||
|
|
9a19322bac | ||
|
|
cca091991f | ||
|
|
3933b8a48f | ||
|
|
a69bd11a77 | ||
|
|
e3d45d22ac | ||
|
|
d6b04166c0 | ||
|
|
dbcb91a118 | ||
|
|
58b1cf42ae | ||
|
|
6221c76578 | ||
|
|
7ffda5d985 | ||
|
|
c40227d93f | ||
|
|
6b411b900e | ||
|
|
95cad01d97 | ||
|
|
0893dafab3 | ||
|
|
118b619da3 | ||
|
|
195bbfe1ba | ||
|
|
8dd3f438d8 | ||
|
|
741cdbe0a3 | ||
|
|
94b39326de | ||
|
|
c3432df3c8 | ||
|
|
c5a84077f0 | ||
|
|
7f36a3ea3a | ||
|
|
edf7200e5d | ||
|
|
8bf0f32b18 | ||
|
|
9d08d51377 | ||
|
|
fb09160d87 | ||
|
|
30796c5dd2 | ||
|
|
69ba93fa16 | ||
|
|
bc06c0b264 | ||
|
|
009794dfd2 | ||
|
|
cbaf84754c | ||
|
|
14d3208b7a | ||
|
|
9cfc11d0df | ||
|
|
499ddeec82 | ||
|
|
63508c8e66 | ||
|
|
0e5b1ed884 | ||
|
|
8469003416 | ||
|
|
fb0e6c758d | ||
|
|
7166b69c5f | ||
|
|
53f92f35d4 | ||
|
|
b4840bddc5 | ||
|
|
3b253d10f3 | ||
|
|
19c3382a19 | ||
|
|
65a1c7b040 | ||
|
|
0cffd955ea | ||
|
|
3847dd9794 | ||
|
|
bc5911e64b | ||
|
|
6f96192e2f | ||
|
|
9f378345fc | ||
|
|
760052b917 | ||
|
|
035fe187c1 | ||
|
|
e1a0caa27b | ||
|
|
e0061f5f37 | ||
|
|
128ddd5237 | ||
|
|
b8094b2073 | ||
|
|
e3924564ed | ||
|
|
3bb8d69760 | ||
|
|
c926c135ca | ||
|
|
8714672ced | ||
|
|
bbc8941d76 | ||
|
|
2a18ab2d76 | ||
|
|
280c41a64d |
@@ -969,7 +969,7 @@
|
||||
<target name="pack-kotlin-test">
|
||||
<pack-runtime-jar jar-name="kotlin-test.jar" implementation-title="${manifest.impl.title.kotlin.test}">
|
||||
<jar-content>
|
||||
<fileset dir="${output}/classes/kotlin.test" includes="**/*"/>
|
||||
<fileset dir="${output}/classes/kotlin.test" includes="**/*" excludes="kotlin/internal/OnlyInputTypes*,kotlin/internal"/>
|
||||
</jar-content>
|
||||
</pack-runtime-jar>
|
||||
</target>
|
||||
|
||||
@@ -2296,22 +2296,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
descriptor = originalIfSamAdapter;
|
||||
}
|
||||
// $default method is not private, so you need no accessor to call it
|
||||
return usesDefaultArguments(resolvedCall)
|
||||
return CallUtilKt.usesDefaultArguments(resolvedCall)
|
||||
? descriptor
|
||||
: context.accessibleDescriptor(descriptor, getSuperCallTarget(resolvedCall.getCall()));
|
||||
}
|
||||
|
||||
private static boolean usesDefaultArguments(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
|
||||
if (valueArguments == null) return false;
|
||||
|
||||
for (ResolvedValueArgument argument : valueArguments) {
|
||||
if (argument instanceof DefaultValueArgument) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue invokeFunction(@NotNull ResolvedCall<?> resolvedCall, @NotNull StackValue receiver) {
|
||||
return invokeFunction(resolvedCall.getCall(), resolvedCall, receiver);
|
||||
@@ -2422,6 +2411,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
||||
|
||||
KotlinType returnType = resolvedCall.getResultingDescriptor().getReturnType();
|
||||
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||
v.aconst(null);
|
||||
v.athrow();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -3804,8 +3799,9 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
return StackValue.operation(resultType, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter v) {
|
||||
SwitchCodegen switchCodegen =
|
||||
SwitchCodegenUtil.buildAppropriateSwitchCodegenIfPossible(expression, isStatement, ExpressionCodegen.this);
|
||||
SwitchCodegen switchCodegen = SwitchCodegenUtil.buildAppropriateSwitchCodegenIfPossible(
|
||||
expression, isStatement, isExhaustive(expression, isStatement), ExpressionCodegen.this
|
||||
);
|
||||
if (switchCodegen != null) {
|
||||
switchCodegen.generate();
|
||||
return Unit.INSTANCE$;
|
||||
@@ -3851,9 +3847,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
}
|
||||
if (!hasElse && nextCondition != null) {
|
||||
v.mark(nextCondition);
|
||||
if (!isStatement) {
|
||||
putUnitInstanceOntoStackForNonExhaustiveWhen(expression);
|
||||
}
|
||||
putUnitInstanceOntoStackForNonExhaustiveWhen(expression, isStatement);
|
||||
}
|
||||
|
||||
markLineNumber(expression, isStatement);
|
||||
@@ -3866,14 +3860,24 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isExhaustive(@NotNull KtWhenExpression whenExpression, boolean isStatement) {
|
||||
if (isStatement) {
|
||||
return Boolean.TRUE.equals(bindingContext.get(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, whenExpression));
|
||||
}
|
||||
else {
|
||||
return Boolean.TRUE.equals(bindingContext.get(BindingContext.EXHAUSTIVE_WHEN, whenExpression));
|
||||
}
|
||||
}
|
||||
|
||||
public void putUnitInstanceOntoStackForNonExhaustiveWhen(
|
||||
@NotNull KtWhenExpression expression
|
||||
@NotNull KtWhenExpression whenExpression,
|
||||
boolean isStatement
|
||||
) {
|
||||
if (Boolean.TRUE.equals(bindingContext.get(BindingContext.EXHAUSTIVE_WHEN, expression))) {
|
||||
if (isExhaustive(whenExpression, isStatement)) {
|
||||
// when() is supposed to be exhaustive
|
||||
genThrow(v, "kotlin/NoWhenBranchMatchedException", null);
|
||||
}
|
||||
else {
|
||||
else if (!isStatement) {
|
||||
// non-exhaustive when() with no else -> Unit must be expected
|
||||
StackValue.putUnitInstance(v);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class InterfaceImplBodyCodegen(
|
||||
|
||||
override fun generateDeclaration() {
|
||||
v.defineClass(
|
||||
myClass, V1_6, ACC_PUBLIC or ACC_FINAL,
|
||||
myClass, V1_6, ACC_PUBLIC or ACC_FINAL or ACC_SUPER,
|
||||
typeMapper.mapDefaultImpls(descriptor).internalName,
|
||||
null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY
|
||||
)
|
||||
|
||||
@@ -297,8 +297,8 @@ public class MultifileClassCodegen(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL
|
||||
private val OPEN_FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC
|
||||
private val FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER
|
||||
private val OPEN_FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_SUPER
|
||||
|
||||
private fun getOnlyPackageFragment(packageFqName: FqName, files: Collection<KtFile>, bindingContext: BindingContext): PackageFragmentDescriptor? {
|
||||
val fragments = SmartList<PackageFragmentDescriptor>()
|
||||
|
||||
@@ -46,7 +46,7 @@ public class MultifileClassPartCodegen(
|
||||
|
||||
override fun generateDeclaration() {
|
||||
v.defineClass(element, Opcodes.V1_6,
|
||||
Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC,
|
||||
Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER,
|
||||
filePartType.internalName,
|
||||
null,
|
||||
"java/lang/Object",
|
||||
|
||||
@@ -63,7 +63,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
@Override
|
||||
protected void generateDeclaration() {
|
||||
v.defineClass(element, V1_6,
|
||||
ACC_PUBLIC | ACC_FINAL,
|
||||
ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
|
||||
packagePartType.getInternalName(),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
|
||||
@@ -86,7 +86,7 @@ public class SamWrapperCodegen {
|
||||
ClassBuilder cv = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(erasedInterfaceFunction), asmType, file);
|
||||
cv.defineClass(file,
|
||||
V1_6,
|
||||
ACC_FINAL,
|
||||
ACC_FINAL | ACC_SUPER,
|
||||
asmType.getInternalName(),
|
||||
null,
|
||||
OBJECT_TYPE.getInternalName(),
|
||||
|
||||
@@ -88,7 +88,7 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
|
||||
|
||||
v.defineClass(scriptDeclaration,
|
||||
V1_6,
|
||||
ACC_PUBLIC,
|
||||
ACC_PUBLIC | ACC_SUPER,
|
||||
classType.getInternalName(),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
|
||||
@@ -23,6 +23,7 @@ import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty;
|
||||
@@ -45,6 +46,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
@@ -1090,6 +1092,12 @@ public abstract class StackValue {
|
||||
else {
|
||||
getter.genInvokeInstruction(v);
|
||||
coerce(getter.getReturnType(), type, v);
|
||||
|
||||
KotlinType returnType = descriptor.getReturnType();
|
||||
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||
v.aconst(null);
|
||||
v.athrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ public final class PsiCodegenPredictor {
|
||||
// TODO: Method won't work for declarations inside companion objects
|
||||
// TODO: Method won't give correct class name for traits implementations
|
||||
|
||||
if (declaration instanceof KtPropertyAccessor) {
|
||||
return getPredefinedJvmInternalName(((KtPropertyAccessor) declaration).getProperty(), fileClassesProvider);
|
||||
}
|
||||
KtDeclaration parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration);
|
||||
|
||||
String parentInternalName;
|
||||
@@ -73,8 +76,8 @@ public final class PsiCodegenPredictor {
|
||||
else {
|
||||
KtFile containingFile = declaration.getContainingKtFile();
|
||||
|
||||
if (declaration instanceof KtNamedFunction) {
|
||||
Name name = ((KtNamedFunction) declaration).getNameAsName();
|
||||
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty) {
|
||||
Name name = ((KtNamedDeclaration) declaration).getNameAsName();
|
||||
return name == null ? null : FileClasses.getFileClassInternalName(fileClassesProvider, containingFile) + "$" + name.asString();
|
||||
}
|
||||
|
||||
|
||||
@@ -404,10 +404,11 @@ public class JetTypeMapper {
|
||||
}
|
||||
|
||||
TypeConstructor constructor = jetType.getConstructor();
|
||||
DeclarationDescriptor descriptor = constructor.getDeclarationDescriptor();
|
||||
if (constructor instanceof IntersectionTypeConstructor) {
|
||||
jetType = CommonSupertypes.commonSupertype(new ArrayList<KotlinType>(constructor.getSupertypes()));
|
||||
constructor = jetType.getConstructor();
|
||||
}
|
||||
DeclarationDescriptor descriptor = constructor.getDeclarationDescriptor();
|
||||
|
||||
if (descriptor == null) {
|
||||
throw new UnsupportedOperationException("no descriptor for type constructor of " + jetType);
|
||||
|
||||
@@ -30,10 +30,11 @@ public class EnumSwitchCodegen extends SwitchCodegen {
|
||||
public EnumSwitchCodegen(
|
||||
@NotNull KtWhenExpression expression,
|
||||
boolean isStatement,
|
||||
boolean isExhaustive,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull WhenByEnumsMapping mapping
|
||||
) {
|
||||
super(expression, isStatement, codegen);
|
||||
super(expression, isStatement, isExhaustive, codegen);
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,10 @@ public class IntegralConstantsSwitchCodegen extends SwitchCodegen {
|
||||
public IntegralConstantsSwitchCodegen(
|
||||
@NotNull KtWhenExpression expression,
|
||||
boolean isStatement,
|
||||
boolean isExhaustive,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
super(expression, isStatement, codegen);
|
||||
super(expression, isStatement, isExhaustive, codegen);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,7 +47,7 @@ public class MappingClassesForWhenByEnumCodegen {
|
||||
cb.defineClass(
|
||||
srcFile,
|
||||
V1_6,
|
||||
ACC_FINAL | ACC_SYNTHETIC | ACC_PUBLIC,
|
||||
ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC,
|
||||
mappingsClass.getInternalName(),
|
||||
null,
|
||||
OBJECT_TYPE.getInternalName(),
|
||||
|
||||
@@ -40,9 +40,10 @@ public class StringSwitchCodegen extends SwitchCodegen {
|
||||
public StringSwitchCodegen(
|
||||
@NotNull KtWhenExpression expression,
|
||||
boolean isStatement,
|
||||
boolean isExhaustive,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
super(expression, isStatement, codegen);
|
||||
super(expression, isStatement, isExhaustive, codegen);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.*;
|
||||
abstract public class SwitchCodegen {
|
||||
protected final KtWhenExpression expression;
|
||||
protected final boolean isStatement;
|
||||
protected final boolean isExhaustive;
|
||||
protected final ExpressionCodegen codegen;
|
||||
protected final BindingContext bindingContext;
|
||||
protected final Type subjectType;
|
||||
@@ -49,10 +50,11 @@ abstract public class SwitchCodegen {
|
||||
|
||||
public SwitchCodegen(
|
||||
@NotNull KtWhenExpression expression, boolean isStatement,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
boolean isExhaustive, @NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
this.expression = expression;
|
||||
this.isStatement = isStatement;
|
||||
this.isExhaustive = isExhaustive;
|
||||
this.codegen = codegen;
|
||||
this.bindingContext = codegen.getBindingContext();
|
||||
|
||||
@@ -70,7 +72,7 @@ abstract public class SwitchCodegen {
|
||||
boolean hasElse = expression.getElseExpression() != null;
|
||||
|
||||
// if there is no else-entry and it's statement then default --- endLabel
|
||||
defaultLabel = (hasElse || !isStatement) ? elseLabel : endLabel;
|
||||
defaultLabel = (hasElse || !isStatement || isExhaustive) ? elseLabel : endLabel;
|
||||
|
||||
generateSubject();
|
||||
|
||||
@@ -79,9 +81,9 @@ abstract public class SwitchCodegen {
|
||||
generateEntries();
|
||||
|
||||
// there is no else-entry but this is not statement, so we should return Unit
|
||||
if (!hasElse && !isStatement) {
|
||||
if (!hasElse && (!isStatement || isExhaustive)) {
|
||||
v.visitLabel(elseLabel);
|
||||
codegen.putUnitInstanceOntoStackForNonExhaustiveWhen(expression);
|
||||
codegen.putUnitInstanceOntoStackForNonExhaustiveWhen(expression, isStatement);
|
||||
}
|
||||
|
||||
codegen.markLineNumber(expression, isStatement);
|
||||
|
||||
@@ -102,6 +102,7 @@ public class SwitchCodegenUtil {
|
||||
public static SwitchCodegen buildAppropriateSwitchCodegenIfPossible(
|
||||
@NotNull KtWhenExpression expression,
|
||||
boolean isStatement,
|
||||
boolean isExhaustive,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
BindingContext bindingContext = codegen.getBindingContext();
|
||||
@@ -114,15 +115,15 @@ public class SwitchCodegenUtil {
|
||||
WhenByEnumsMapping mapping = codegen.getBindingContext().get(CodegenBinding.MAPPING_FOR_WHEN_BY_ENUM, expression);
|
||||
|
||||
if (mapping != null) {
|
||||
return new EnumSwitchCodegen(expression, isStatement, codegen, mapping);
|
||||
return new EnumSwitchCodegen(expression, isStatement, isExhaustive, codegen, mapping);
|
||||
}
|
||||
|
||||
if (isIntegralConstantsSwitch(expression, subjectType, bindingContext)) {
|
||||
return new IntegralConstantsSwitchCodegen(expression, isStatement, codegen);
|
||||
return new IntegralConstantsSwitchCodegen(expression, isStatement, isExhaustive, codegen);
|
||||
}
|
||||
|
||||
if (isStringConstantsSwitch(expression, subjectType, bindingContext)) {
|
||||
return new StringSwitchCodegen(expression, isStatement, codegen);
|
||||
return new StringSwitchCodegen(expression, isStatement, isExhaustive, codegen);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.client
|
||||
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacade
|
||||
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
|
||||
import org.jetbrains.kotlin.daemon.common.RmiFriendlyCompilationCancelledException
|
||||
import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
|
||||
import org.jetbrains.kotlin.incremental.components.LookupInfo
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
@@ -80,6 +82,13 @@ class CompilerCallbackServicesFacadeServer(
|
||||
override fun lookupTracker_isDoNothing(): Boolean = lookupTracker_isDoNothing
|
||||
|
||||
override fun compilationCanceledStatus_checkCanceled() {
|
||||
compilationCancelledStatus!!.checkCanceled()
|
||||
try {
|
||||
compilationCancelledStatus!!.checkCanceled()
|
||||
}
|
||||
catch (e: ProcessCanceledException) {
|
||||
// avoid passing exceptions that may have different serialVersionUID on across rmi border
|
||||
// TODO: doublecheck whether we need to distinguish different cancellation exceptions
|
||||
throw RmiFriendlyCompilationCancelledException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.daemon.common
|
||||
import org.jetbrains.kotlin.incremental.components.LookupInfo
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import java.io.Serializable
|
||||
import java.rmi.Remote
|
||||
import java.rmi.RemoteException
|
||||
|
||||
@@ -81,7 +82,13 @@ interface CompilerCallbackServicesFacade : Remote {
|
||||
|
||||
// ----------------------------------------------------
|
||||
// CompilationCanceledStatus
|
||||
@Throws(RemoteException::class)
|
||||
@Throws(RemoteException::class, RmiFriendlyCompilationCancelledException::class)
|
||||
fun compilationCanceledStatus_checkCanceled(): Unit
|
||||
}
|
||||
|
||||
|
||||
class RmiFriendlyCompilationCancelledException: Exception(), Serializable {
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L // just a random number, but should never be changed to avoid deserialization problems
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacade
|
||||
import org.jetbrains.kotlin.daemon.common.DummyProfiler
|
||||
import org.jetbrains.kotlin.daemon.common.Profiler
|
||||
import org.jetbrains.kotlin.daemon.common.RmiFriendlyCompilationCancelledException
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val CANCELED_STATUS_CHECK_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(100)
|
||||
@@ -30,7 +32,12 @@ class RemoteCompilationCanceledStatusClient(val facade: CompilerCallbackServices
|
||||
val curNanos = System.nanoTime()
|
||||
if (curNanos - lastChecked > CANCELED_STATUS_CHECK_THRESHOLD_NS) {
|
||||
profiler.withMeasure(this) {
|
||||
facade.compilationCanceledStatus_checkCanceled()
|
||||
try {
|
||||
facade.compilationCanceledStatus_checkCanceled()
|
||||
}
|
||||
catch (e: RmiFriendlyCompilationCancelledException) {
|
||||
throw CompilationCanceledException()
|
||||
}
|
||||
}
|
||||
lastChecked = curNanos
|
||||
}
|
||||
|
||||
@@ -27,10 +27,8 @@ import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||
|
||||
public class RuntimeAssertionInfo(public val needNotNullAssertion: Boolean, public val message: String) {
|
||||
public interface DataFlowExtras {
|
||||
@@ -53,7 +51,7 @@ public class RuntimeAssertionInfo(public val needNotNullAssertion: Boolean, publ
|
||||
dataFlowExtras: DataFlowExtras
|
||||
): RuntimeAssertionInfo? {
|
||||
fun assertNotNull(): Boolean {
|
||||
if (expectedType.isError() || expressionType.isError()) return false
|
||||
if (expectedType.isError || expressionType.isError) return false
|
||||
|
||||
// T : Any, T! = T..T?
|
||||
// Let T$ will be copy of T! with enhanced nullability.
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition;
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
@@ -45,6 +46,11 @@ public class SamAdapterOverridabilityCondition implements ExternalOverridability
|
||||
return subOriginal == null ? Result.UNKNOWN : Result.INCOMPATIBLE; // DECLARATION can override anything
|
||||
}
|
||||
|
||||
OverridingUtil.OverrideCompatibilityInfo basicResult = OverridingUtil.DEFAULT
|
||||
.isOverridableByWithoutExternalConditions(superDescriptor, subDescriptor, /* checkReturnType = */ false);
|
||||
|
||||
if (basicResult.getResult() != OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE) return Result.UNKNOWN;
|
||||
|
||||
// inheritor if SYNTHESIZED can override inheritor of SYNTHESIZED if their originals have same erasure
|
||||
return equalErasure(superOriginal, subOriginal) ? Result.UNKNOWN : Result.INCOMPATIBLE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getSuperCallExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.usesDefaultArguments
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
|
||||
class SuperCallWithDefaultArgumentsChecker : CallChecker {
|
||||
override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
|
||||
val superCallExpression = getSuperCallExpression(resolvedCall.call)
|
||||
if (superCallExpression == null || !resolvedCall.usesDefaultArguments()) return
|
||||
context.trace.report(ErrorsJvm.SUPER_CALL_WITH_DEFAULT_PARAMETERS.on(superCallExpression.parent, resolvedCall.resultingDescriptor.name.asString()))
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(ErrorsJvm.UPPER_BOUND_CANNOT_BE_ARRAY, "Upper bound of a type parameter cannot be an array");
|
||||
|
||||
MAP.put(ErrorsJvm.INAPPLICABLE_JVM_FIELD, "{0}", Renderers.TO_STRING);
|
||||
|
||||
MAP.put(ErrorsJvm.SUPER_CALL_WITH_DEFAULT_PARAMETERS, "Super-calls with default arguments are not allowed. Please specify all arguments of ''super.{0}'' explicitly", Renderers.TO_STRING);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -79,6 +79,8 @@ public interface ErrorsJvm {
|
||||
|
||||
DiagnosticFactory0<PsiElement> UPPER_BOUND_CANNOT_BE_ARRAY = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> SUPER_CALL_WITH_DEFAULT_PARAMETERS = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
enum NullabilityInformationSource {
|
||||
KOTLIN {
|
||||
@NotNull
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.jvm.RuntimeAssertionsTypeChecker
|
||||
import org.jetbrains.kotlin.load.kotlin.JavaAnnotationCallChecker
|
||||
import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeFunChecker
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.checkers.SuperCallWithDefaultArgumentsChecker
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmOverloadFilter
|
||||
import org.jetbrains.kotlin.resolve.jvm.checkers.*
|
||||
import org.jetbrains.kotlin.synthetic.JavaSyntheticScopes
|
||||
@@ -48,7 +49,8 @@ public object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
TraitDefaultMethodCallChecker(),
|
||||
JavaClassOnCompanionChecker(),
|
||||
ProtectedInSuperClassCompanionCallChecker(),
|
||||
UnsupportedSyntheticCallableReferenceChecker()
|
||||
UnsupportedSyntheticCallableReferenceChecker(),
|
||||
SuperCallWithDefaultArgumentsChecker()
|
||||
),
|
||||
|
||||
additionalTypeCheckers = listOf(
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cfg
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import java.util.*
|
||||
|
||||
open class ControlFlowInfo<D> internal constructor(protected val map: MutableMap<VariableDescriptor, D> = hashMapOf()) :
|
||||
MutableMap<VariableDescriptor, D> by map {
|
||||
open fun copy() = ControlFlowInfo(HashMap(map))
|
||||
|
||||
fun retainAll(predicate: (VariableDescriptor) -> Boolean): ControlFlowInfo<D> {
|
||||
map.keys.retainAll(predicate)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*>)?.map
|
||||
|
||||
override fun hashCode() = map.hashCode()
|
||||
|
||||
override fun toString() = map.toString()
|
||||
}
|
||||
|
||||
class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) :
|
||||
ControlFlowInfo<VariableControlFlowState>(map) {
|
||||
override fun copy() = InitControlFlowInfo(HashMap(map))
|
||||
|
||||
// this = output of EXHAUSTIVE_WHEN_ELSE instruction
|
||||
// merge = input of MergeInstruction
|
||||
// returns true if definite initialization in when happens here
|
||||
fun checkDefiniteInitializationInWhen(merge: InitControlFlowInfo): Boolean {
|
||||
for (entry in entries) {
|
||||
if (entry.value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
|
||||
merge[entry.key]?.initState == InitState.INITIALIZED) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class UseControlFlowInfo(map: MutableMap<VariableDescriptor, VariableUseState> = hashMapOf()) :
|
||||
ControlFlowInfo<VariableUseState>(map) {
|
||||
override fun copy() = UseControlFlowInfo(HashMap(map))
|
||||
}
|
||||
|
||||
enum class InitState(private val s: String) {
|
||||
// Definitely initialized
|
||||
INITIALIZED("I"),
|
||||
// Fake initializer in else branch of "exhaustive when without else", see MagicKind.EXHAUSTIVE_WHEN_ELSE
|
||||
INITIALIZED_EXHAUSTIVELY("IE"),
|
||||
// Initialized in some branches, not initialized in other branches
|
||||
UNKNOWN("I?"),
|
||||
// Definitely not initialized
|
||||
NOT_INITIALIZED("");
|
||||
|
||||
fun merge(other: InitState): InitState {
|
||||
// X merge X = X
|
||||
// X merge IE = IE merge X = X
|
||||
// else X merge Y = I?
|
||||
if (this == other || other == INITIALIZED_EXHAUSTIVELY) return this
|
||||
if (this == INITIALIZED_EXHAUSTIVELY) return other
|
||||
return UNKNOWN
|
||||
}
|
||||
|
||||
override fun toString() = s
|
||||
}
|
||||
|
||||
class VariableControlFlowState private constructor(val initState: InitState, val isDeclared: Boolean) {
|
||||
|
||||
fun definitelyInitialized(): Boolean {
|
||||
return initState == InitState.INITIALIZED
|
||||
}
|
||||
|
||||
fun mayBeInitialized(): Boolean {
|
||||
return initState != InitState.NOT_INITIALIZED
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
if (initState == InitState.NOT_INITIALIZED && !isDeclared) return "-"
|
||||
return "$initState${if (isDeclared) "D" else ""}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val VS_IT = VariableControlFlowState(InitState.INITIALIZED, true)
|
||||
private val VS_IF = VariableControlFlowState(InitState.INITIALIZED, false)
|
||||
private val VS_ET = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, true)
|
||||
private val VS_EF = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, false)
|
||||
private val VS_UT = VariableControlFlowState(InitState.UNKNOWN, true)
|
||||
private val VS_UF = VariableControlFlowState(InitState.UNKNOWN, false)
|
||||
private val VS_NT = VariableControlFlowState(InitState.NOT_INITIALIZED, true)
|
||||
private val VS_NF = VariableControlFlowState(InitState.NOT_INITIALIZED, false)
|
||||
|
||||
fun create(initState: InitState, isDeclared: Boolean): VariableControlFlowState =
|
||||
when (initState) {
|
||||
InitState.INITIALIZED -> if (isDeclared) VS_IT else VS_IF
|
||||
InitState.INITIALIZED_EXHAUSTIVELY -> if (isDeclared) VS_ET else VS_EF
|
||||
InitState.UNKNOWN -> if (isDeclared) VS_UT else VS_UF
|
||||
InitState.NOT_INITIALIZED -> if (isDeclared) VS_NT else VS_NF
|
||||
}
|
||||
|
||||
fun createInitializedExhaustively(isDeclared: Boolean): VariableControlFlowState {
|
||||
return create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared)
|
||||
}
|
||||
|
||||
fun create(isInitialized: Boolean, isDeclared: Boolean = false): VariableControlFlowState {
|
||||
return create(if (isInitialized) InitState.INITIALIZED else InitState.NOT_INITIALIZED, isDeclared)
|
||||
}
|
||||
|
||||
fun create(isDeclaredHere: Boolean, mergedEdgesData: VariableControlFlowState?): VariableControlFlowState {
|
||||
return create(true, isDeclaredHere || mergedEdgesData != null && mergedEdgesData.isDeclared)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class VariableUseState(private val priority: Int) {
|
||||
READ(3),
|
||||
WRITTEN_AFTER_READ(2),
|
||||
ONLY_WRITTEN_NEVER_READ(1),
|
||||
UNUSED(0);
|
||||
|
||||
fun merge(variableUseState: VariableUseState?): VariableUseState {
|
||||
if (variableUseState == null || priority > variableUseState.priority) return this
|
||||
return variableUseState
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun isUsed(variableUseState: VariableUseState?): Boolean {
|
||||
return variableUseState != null && variableUseState != UNUSED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cfg;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -28,8 +29,6 @@ import kotlin.jvm.functions.Function3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.cfg.PseudocodeVariablesData.VariableControlFlowState;
|
||||
import org.jetbrains.kotlin.cfg.PseudocodeVariablesData.VariableUseState;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil;
|
||||
@@ -65,7 +64,7 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.cfg.PseudocodeVariablesData.VariableUseState.*;
|
||||
import static org.jetbrains.kotlin.cfg.VariableUseState.*;
|
||||
import static org.jetbrains.kotlin.cfg.TailRecursionKind.*;
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.UNREACHABLE_CODE;
|
||||
@@ -303,7 +302,7 @@ public class ControlFlowInformationProvider {
|
||||
final boolean processClassOrObject = subroutine instanceof KtClassOrObject;
|
||||
|
||||
PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
|
||||
Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializers =
|
||||
Map<Instruction, Edges<InitControlFlowInfo>> initializers =
|
||||
pseudocodeVariablesData.getVariableInitializers();
|
||||
final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
|
||||
final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo();
|
||||
@@ -352,8 +351,7 @@ public class ControlFlowInformationProvider {
|
||||
public void recordInitializedVariables() {
|
||||
PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
|
||||
Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
|
||||
Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializers =
|
||||
pseudocodeVariablesData.getVariableInitializers();
|
||||
Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
|
||||
recordInitializedVariables(pseudocode, initializers);
|
||||
for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
|
||||
recordInitializedVariables(instruction.getBody(), initializers);
|
||||
@@ -478,7 +476,7 @@ public class ControlFlowInformationProvider {
|
||||
}
|
||||
|
||||
private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull KtExpression expression) {
|
||||
if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared
|
||||
if (!ctxt.enterInitState.isDeclared() && !ctxt.exitInitState.isDeclared()
|
||||
&& !ctxt.enterInitState.mayBeInitialized() && ctxt.exitInitState.mayBeInitialized()) {
|
||||
report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
|
||||
return true;
|
||||
@@ -519,9 +517,9 @@ public class ControlFlowInformationProvider {
|
||||
|
||||
private void recordInitializedVariables(
|
||||
@NotNull Pseudocode pseudocode,
|
||||
@NotNull Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializersMap
|
||||
@NotNull Map<Instruction, Edges<InitControlFlowInfo>> initializersMap
|
||||
) {
|
||||
Edges<Map<VariableDescriptor, VariableControlFlowState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
|
||||
Edges<InitControlFlowInfo> initializers = initializersMap.get(pseudocode.getExitInstruction());
|
||||
if (initializers == null) return;
|
||||
Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false);
|
||||
for (VariableDescriptor variable : declaredVariables) {
|
||||
@@ -538,8 +536,7 @@ public class ControlFlowInformationProvider {
|
||||
|
||||
public void markUnusedVariables() {
|
||||
final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
|
||||
Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData =
|
||||
pseudocodeVariablesData.getVariableUseStatusData();
|
||||
Map<Instruction, Edges<UseControlFlowInfo>> variableStatusData = pseudocodeVariablesData.getVariableUseStatusData();
|
||||
final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
|
||||
InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
|
||||
new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>>() {
|
||||
@@ -560,7 +557,7 @@ public class ControlFlowInformationProvider {
|
||||
|| !ExpressionTypingUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) {
|
||||
return;
|
||||
}
|
||||
PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
|
||||
VariableUseState variableUseState = in.get(variableDescriptor);
|
||||
if (instruction instanceof WriteValueInstruction) {
|
||||
if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
|
||||
KtElement element = ((WriteValueInstruction) instruction).getElement();
|
||||
@@ -705,10 +702,17 @@ public class ControlFlowInformationProvider {
|
||||
for (KtElement element : instruction.getOwner().getValueElements(value)) {
|
||||
if (!(element instanceof KtIfExpression)) continue;
|
||||
KtIfExpression ifExpression = (KtIfExpression) element;
|
||||
if (ifExpression.getThen() != null && ifExpression.getElse() != null) continue;
|
||||
|
||||
if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) {
|
||||
trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
|
||||
KtExpression thenExpression = ifExpression.getThen();
|
||||
KtExpression elseExpression = ifExpression.getElse();
|
||||
|
||||
if (thenExpression == null || elseExpression == null) {
|
||||
trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
|
||||
}
|
||||
else {
|
||||
checkImplicitCastOnConditionalExpression(ifExpression, ImmutableList.of(thenExpression, elseExpression));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -716,17 +720,72 @@ public class ControlFlowInformationProvider {
|
||||
);
|
||||
}
|
||||
|
||||
private void checkImplicitCastOnConditionalExpression(
|
||||
@NotNull KtExpression expression,
|
||||
@NotNull Collection<KtExpression> branchExpressions
|
||||
) {
|
||||
KotlinType expectedExpressionType = trace.get(EXPECTED_EXPRESSION_TYPE, expression);
|
||||
if (expectedExpressionType != null) return;
|
||||
|
||||
KotlinType expressionType = trace.getType(expression);
|
||||
if (expressionType == null) {
|
||||
return;
|
||||
}
|
||||
if (KotlinBuiltIns.isAnyOrNullableAny(expressionType)) {
|
||||
for (KtExpression branchExpression : branchExpressions) {
|
||||
KotlinType branchType = trace.getType(branchExpression);
|
||||
if (branchType == null || KotlinBuiltIns.isAnyOrNullableAny(branchType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
|
||||
}
|
||||
else if (KotlinBuiltIns.isUnit(expressionType)) {
|
||||
trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
|
||||
}
|
||||
}
|
||||
|
||||
public void markWhenWithoutElse() {
|
||||
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
|
||||
PseudocodeTraverserKt.traverse(
|
||||
pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
|
||||
@Override
|
||||
public void execute(@NotNull Instruction instruction) {
|
||||
if (instruction instanceof MagicInstruction) {
|
||||
MagicInstruction magicInstruction = (MagicInstruction) instruction;
|
||||
if (magicInstruction.getKind() == MagicKind.EXHAUSTIVE_WHEN_ELSE) {
|
||||
Instruction next = magicInstruction.getNext();
|
||||
if (next instanceof MergeInstruction) {
|
||||
MergeInstruction mergeInstruction = (MergeInstruction) next;
|
||||
if (initializers.containsKey(mergeInstruction) && initializers.containsKey(magicInstruction)) {
|
||||
InitControlFlowInfo mergeInfo = initializers.get(mergeInstruction).getIncoming();
|
||||
InitControlFlowInfo magicInfo = initializers.get(magicInstruction).getOutgoing();
|
||||
if (mergeInstruction.getElement() instanceof KtWhenExpression &&
|
||||
magicInfo.checkDefiniteInitializationInWhen(mergeInfo)) {
|
||||
trace.record(IMPLICIT_EXHAUSTIVE_WHEN, (KtWhenExpression) mergeInstruction.getElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PseudoValue value = instruction instanceof InstructionWithValue
|
||||
? ((InstructionWithValue) instruction).getOutputValue()
|
||||
: null;
|
||||
for (KtElement element : instruction.getOwner().getValueElements(value)) {
|
||||
if (!(element instanceof KtWhenExpression)) continue;
|
||||
KtWhenExpression whenExpression = (KtWhenExpression) element;
|
||||
|
||||
if (BindingContextUtilsKt.isUsedAsExpression(whenExpression, trace.getBindingContext())) {
|
||||
List<KtExpression> branchExpressions = new ArrayList<KtExpression>(whenExpression.getEntries().size() + 1);
|
||||
for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
|
||||
branchExpressions.add(whenEntry.getExpression());
|
||||
}
|
||||
if (whenExpression.getElseExpression() != null) {
|
||||
branchExpressions.add(whenExpression.getElseExpression());
|
||||
}
|
||||
checkImplicitCastOnConditionalExpression(whenExpression, branchExpressions);
|
||||
}
|
||||
|
||||
if (whenExpression.getElseExpression() != null) continue;
|
||||
|
||||
if (WhenChecker.mustHaveElse(whenExpression, trace)) {
|
||||
@@ -741,6 +800,8 @@ public class ControlFlowInformationProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1297,7 +1297,7 @@ public class ControlFlowProcessor {
|
||||
// For the last entry of exhaustive when,
|
||||
// attempt to jump further should lead to error, not to "done"
|
||||
if (!iterator.hasNext() && WhenChecker.isWhenExhaustive(expression, trace)) {
|
||||
builder.jumpToError(expression);
|
||||
builder.magic(expression, null, Collections.<PseudoValue>emptyList(), MagicKind.EXHAUSTIVE_WHEN_ELSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cfg.pseudocodeTraverser
|
||||
|
||||
import org.jetbrains.kotlin.cfg.ControlFlowInfo
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.*
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
|
||||
@@ -54,16 +55,16 @@ public fun <D> Pseudocode.traverse(
|
||||
}
|
||||
}
|
||||
|
||||
public fun <D> Pseudocode.collectData(
|
||||
public fun <I : ControlFlowInfo<*>> Pseudocode.collectData(
|
||||
traversalOrder: TraversalOrder,
|
||||
mergeDataWithLocalDeclarations: Boolean,
|
||||
mergeEdges: (Instruction, Collection<D>) -> Edges<D>,
|
||||
updateEdge: (Instruction, Instruction, D) -> D,
|
||||
initialDataValue: D
|
||||
): Map<Instruction, Edges<D>> {
|
||||
val edgesMap = LinkedHashMap<Instruction, Edges<D>>()
|
||||
initializeEdgesMap(edgesMap, initialDataValue)
|
||||
edgesMap.put(getStartInstruction(traversalOrder), Edges(initialDataValue, initialDataValue))
|
||||
mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
|
||||
updateEdge: (Instruction, Instruction, I) -> I,
|
||||
initialInfo: I
|
||||
): Map<Instruction, Edges<I>> {
|
||||
val edgesMap = LinkedHashMap<Instruction, Edges<I>>()
|
||||
initializeEdgesMap(edgesMap, initialInfo)
|
||||
edgesMap.put(getStartInstruction(traversalOrder), Edges(initialInfo, initialInfo))
|
||||
|
||||
val changed = BooleanArray(1)
|
||||
changed[0] = true
|
||||
@@ -76,26 +77,26 @@ public fun <D> Pseudocode.collectData(
|
||||
return edgesMap
|
||||
}
|
||||
|
||||
private fun <D> Pseudocode.initializeEdgesMap(
|
||||
edgesMap: MutableMap<Instruction, Edges<D>>,
|
||||
initialDataValue: D
|
||||
private fun <I> Pseudocode.initializeEdgesMap(
|
||||
edgesMap: MutableMap<Instruction, Edges<I>>,
|
||||
initialInfo: I
|
||||
) {
|
||||
val instructions = getInstructions()
|
||||
val initialEdge = Edges(initialDataValue, initialDataValue)
|
||||
val initialEdge = Edges(initialInfo, initialInfo)
|
||||
for (instruction in instructions) {
|
||||
edgesMap.put(instruction, initialEdge)
|
||||
if (instruction is LocalFunctionDeclarationInstruction) {
|
||||
instruction.body.initializeEdgesMap(edgesMap, initialDataValue)
|
||||
instruction.body.initializeEdgesMap(edgesMap, initialInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <D> Pseudocode.collectDataFromSubgraph(
|
||||
private fun <I : ControlFlowInfo<*>> Pseudocode.collectDataFromSubgraph(
|
||||
traversalOrder: TraversalOrder,
|
||||
mergeDataWithLocalDeclarations: Boolean,
|
||||
edgesMap: MutableMap<Instruction, Edges<D>>,
|
||||
mergeEdges: (Instruction, Collection<D>) -> Edges<D>,
|
||||
updateEdge: (Instruction, Instruction, D) -> D,
|
||||
edgesMap: MutableMap<Instruction, Edges<I>>,
|
||||
mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
|
||||
updateEdge: (Instruction, Instruction, I) -> I,
|
||||
previousSubGraphInstructions: Collection<Instruction>,
|
||||
changed: BooleanArray,
|
||||
isLocal: Boolean
|
||||
@@ -120,8 +121,8 @@ private fun <D> Pseudocode.collectDataFromSubgraph(
|
||||
val previousInstructions = getPreviousIncludingSubGraphInstructions()
|
||||
|
||||
fun updateEdgeDataForInstruction(
|
||||
previousValue: Edges<D>?,
|
||||
newValue: Edges<D>?
|
||||
previousValue: Edges<I>?,
|
||||
newValue: Edges<I>?
|
||||
) {
|
||||
if (previousValue != newValue && newValue != null) {
|
||||
changed[0] = true
|
||||
@@ -151,7 +152,7 @@ private fun <D> Pseudocode.collectDataFromSubgraph(
|
||||
}
|
||||
val previousDataValue = edgesMap.get(instruction)
|
||||
|
||||
val incomingEdgesData = HashSet<D>()
|
||||
val incomingEdgesData = HashSet<I>()
|
||||
|
||||
for (previousInstruction in previousInstructions) {
|
||||
val previousData = edgesMap.get(previousInstruction)
|
||||
@@ -167,6 +168,11 @@ private fun <D> Pseudocode.collectDataFromSubgraph(
|
||||
|
||||
data class Edges<T>(val incoming: T, val outgoing: T)
|
||||
|
||||
enum class TraverseInstructionResult {
|
||||
CONTINUE,
|
||||
SKIP,
|
||||
HALT
|
||||
}
|
||||
|
||||
// returns false when interrupted by handler
|
||||
public fun traverseFollowingInstructions(
|
||||
@@ -174,7 +180,7 @@ public fun traverseFollowingInstructions(
|
||||
visited: MutableSet<Instruction>,
|
||||
order: TraversalOrder,
|
||||
// true to continue traversal
|
||||
handler: ((Instruction) -> Boolean)?
|
||||
handler: ((Instruction) -> TraverseInstructionResult)?
|
||||
): Boolean {
|
||||
val stack = ArrayDeque<Instruction>()
|
||||
stack.push(rootInstruction)
|
||||
@@ -182,9 +188,11 @@ public fun traverseFollowingInstructions(
|
||||
while (!stack.isEmpty()) {
|
||||
val instruction = stack.pop()
|
||||
if (!visited.add(instruction)) continue
|
||||
if (handler != null && !handler(instruction)) return false
|
||||
|
||||
instruction.getNextInstructions(order).forEach { stack.push(it) }
|
||||
when (handler?.let { it(instruction) } ?: TraverseInstructionResult.CONTINUE) {
|
||||
TraverseInstructionResult.CONTINUE -> instruction.getNextInstructions(order).forEach { stack.push(it) }
|
||||
TraverseInstructionResult.SKIP -> {}
|
||||
TraverseInstructionResult.HALT -> return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import java.util.ArrayList
|
||||
import java.util.Collections
|
||||
import java.util.HashMap
|
||||
|
||||
public class PseudocodeVariableDataCollector(
|
||||
@@ -36,40 +35,38 @@ public class PseudocodeVariableDataCollector(
|
||||
) {
|
||||
val lexicalScopeVariableInfo = computeLexicalScopeVariableInfo(pseudocode)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <D> collectData(
|
||||
public fun <I : ControlFlowInfo<*>> collectData(
|
||||
traversalOrder: TraversalOrder,
|
||||
mergeDataWithLocalDeclarations: Boolean,
|
||||
instructionDataMergeStrategy: InstructionDataMergeStrategy<D>
|
||||
): MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, D>>> {
|
||||
val result = pseudocode.collectData(
|
||||
initialInfo: I,
|
||||
instructionDataMergeStrategy: (Instruction, Collection<I>) -> Edges<I>
|
||||
): Map<Instruction, Edges<I>> {
|
||||
return pseudocode.collectData(
|
||||
traversalOrder, mergeDataWithLocalDeclarations,
|
||||
//see KT-4605
|
||||
instructionDataMergeStrategy as
|
||||
(Instruction, Collection<Map<VariableDescriptor, D>>) -> Edges<Map<VariableDescriptor, D>>,
|
||||
{ from, to, data -> filterOutVariablesOutOfScope(from, to, data)},
|
||||
Collections.emptyMap<VariableDescriptor, D>())
|
||||
//see KT-4605
|
||||
return result as MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, D>>>
|
||||
instructionDataMergeStrategy,
|
||||
{ from, to, info -> filterOutVariablesOutOfScope(from, to, info) },
|
||||
initialInfo
|
||||
)
|
||||
}
|
||||
|
||||
private fun <D> filterOutVariablesOutOfScope(
|
||||
private fun <I : ControlFlowInfo<*>> filterOutVariablesOutOfScope(
|
||||
from: Instruction,
|
||||
to: Instruction,
|
||||
data: Map<VariableDescriptor, D>
|
||||
): Map<VariableDescriptor, D> {
|
||||
info: I
|
||||
): I {
|
||||
// If an edge goes from deeper lexical scope to a less deep one, this means that it points outside of the deeper scope.
|
||||
val toDepth = to.lexicalScope.depth
|
||||
if (toDepth >= from.lexicalScope.depth) return data
|
||||
if (toDepth >= from.lexicalScope.depth) return info
|
||||
|
||||
// Variables declared in an inner (deeper) scope can't be accessed from an outer scope.
|
||||
// Thus they can be filtered out upon leaving the inner scope.
|
||||
return data.filterKeys { variable ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return info.copy().retainAll { variable ->
|
||||
val lexicalScope = lexicalScopeVariableInfo.declaredIn[variable]
|
||||
// '-1' for variables declared outside this pseudocode
|
||||
val depth = lexicalScope?.depth ?: -1
|
||||
depth <= toDepth
|
||||
}
|
||||
} as I
|
||||
}
|
||||
|
||||
fun computeLexicalScopeVariableInfo(pseudocode: Pseudocode): LexicalScopeVariableInfo {
|
||||
@@ -96,10 +93,6 @@ public class PseudocodeVariableDataCollector(
|
||||
}
|
||||
}
|
||||
|
||||
//todo may be a type alias
|
||||
interface InstructionDataMergeStrategy<D> :
|
||||
(Instruction, Collection<MutableMap<VariableDescriptor, D>>) -> Edges<MutableMap<VariableDescriptor, D>>
|
||||
|
||||
public interface LexicalScopeVariableInfo {
|
||||
val declaredIn : Map<VariableDescriptor, LexicalScope>
|
||||
val scopeVariables : Map<LexicalScope, Collection<VariableDescriptor>>
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cfg;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.LexicalScope;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtProperty;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD;
|
||||
import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
|
||||
|
||||
public class PseudocodeVariablesData {
|
||||
private final Pseudocode pseudocode;
|
||||
private final BindingContext bindingContext;
|
||||
private final PseudocodeVariableDataCollector pseudocodeVariableDataCollector;
|
||||
|
||||
private final Map<Pseudocode, Set<VariableDescriptor>> declaredVariablesForDeclaration = Maps.newHashMap();
|
||||
|
||||
private Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> variableInitializers;
|
||||
|
||||
public PseudocodeVariablesData(@NotNull Pseudocode pseudocode, @NotNull BindingContext bindingContext) {
|
||||
this.pseudocode = pseudocode;
|
||||
this.bindingContext = bindingContext;
|
||||
this.pseudocodeVariableDataCollector = new PseudocodeVariableDataCollector(bindingContext, pseudocode);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Pseudocode getPseudocode() {
|
||||
return pseudocode;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LexicalScopeVariableInfo getLexicalScopeVariableInfo() {
|
||||
return pseudocodeVariableDataCollector.getLexicalScopeVariableInfo();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<VariableDescriptor> getDeclaredVariables(@NotNull Pseudocode pseudocode, boolean includeInsideLocalDeclarations) {
|
||||
if (!includeInsideLocalDeclarations) {
|
||||
return getUpperLevelDeclaredVariables(pseudocode);
|
||||
}
|
||||
Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(pseudocode));
|
||||
|
||||
for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : pseudocode.getLocalDeclarations()) {
|
||||
Pseudocode localPseudocode = localFunctionDeclarationInstruction.getBody();
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(localPseudocode));
|
||||
}
|
||||
return declaredVariables;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<VariableDescriptor> getUpperLevelDeclaredVariables(@NotNull Pseudocode pseudocode) {
|
||||
Set<VariableDescriptor> declaredVariables = declaredVariablesForDeclaration.get(pseudocode);
|
||||
if (declaredVariables == null) {
|
||||
declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode);
|
||||
declaredVariablesForDeclaration.put(pseudocode, declaredVariables);
|
||||
}
|
||||
return declaredVariables;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<VariableDescriptor> computeDeclaredVariablesForPseudocode(Pseudocode pseudocode) {
|
||||
Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
||||
for (Instruction instruction : pseudocode.getInstructions()) {
|
||||
if (instruction instanceof VariableDeclarationInstruction) {
|
||||
KtDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement);
|
||||
if (descriptor != null) {
|
||||
assert descriptor instanceof VariableDescriptor;
|
||||
declaredVariables.add((VariableDescriptor) descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(declaredVariables);
|
||||
}
|
||||
|
||||
// variable initializers
|
||||
|
||||
@NotNull
|
||||
public Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> getVariableInitializers() {
|
||||
if (variableInitializers == null) {
|
||||
variableInitializers = computeVariableInitializers();
|
||||
}
|
||||
|
||||
return variableInitializers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> computeVariableInitializers() {
|
||||
|
||||
final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariableDataCollector.getLexicalScopeVariableInfo();
|
||||
|
||||
return pseudocodeVariableDataCollector.collectData(
|
||||
FORWARD, /*mergeDataWithLocalDeclarations=*/ true,
|
||||
new InstructionDataMergeStrategy<VariableControlFlowState>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Edges<Map<VariableDescriptor, VariableControlFlowState>> invoke(
|
||||
@NotNull Instruction instruction,
|
||||
@NotNull Collection<? extends Map<VariableDescriptor, VariableControlFlowState>> incomingEdgesData
|
||||
) {
|
||||
|
||||
Map<VariableDescriptor, VariableControlFlowState> enterInstructionData =
|
||||
mergeIncomingEdgesDataForInitializers(incomingEdgesData);
|
||||
Map<VariableDescriptor, VariableControlFlowState> exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
|
||||
instruction, enterInstructionData, lexicalScopeVariableInfo);
|
||||
return new Edges<Map<VariableDescriptor, VariableControlFlowState>>(enterInstructionData, exitInstructionData);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static VariableControlFlowState getDefaultValueForInitializers(
|
||||
@NotNull VariableDescriptor variable,
|
||||
@NotNull Instruction instruction,
|
||||
@NotNull LexicalScopeVariableInfo lexicalScopeVariableInfo
|
||||
) {
|
||||
//todo: think of replacing it with "MapWithDefaultValue"
|
||||
LexicalScope declaredIn = lexicalScopeVariableInfo.getDeclaredIn().get(variable);
|
||||
boolean declaredOutsideThisDeclaration =
|
||||
declaredIn == null //declared outside this pseudocode
|
||||
|| declaredIn.getLexicalScopeForContainingDeclaration() != instruction.getLexicalScope().getLexicalScopeForContainingDeclaration();
|
||||
return VariableControlFlowState.create(/*initState=*/declaredOutsideThisDeclaration);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Map<VariableDescriptor, VariableControlFlowState> mergeIncomingEdgesDataForInitializers(
|
||||
@NotNull Collection<? extends Map<VariableDescriptor, VariableControlFlowState>> incomingEdgesData
|
||||
) {
|
||||
Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
|
||||
for (Map<VariableDescriptor, VariableControlFlowState> edgeData : incomingEdgesData) {
|
||||
variablesInScope.addAll(edgeData.keySet());
|
||||
}
|
||||
|
||||
Map<VariableDescriptor, VariableControlFlowState> enterInstructionData = Maps.newHashMap();
|
||||
for (VariableDescriptor variable : variablesInScope) {
|
||||
TriInitState initState = null;
|
||||
boolean isDeclared = true;
|
||||
for (Map<VariableDescriptor, VariableControlFlowState> edgeData : incomingEdgesData) {
|
||||
VariableControlFlowState varControlFlowState = edgeData.get(variable);
|
||||
if (varControlFlowState != null) {
|
||||
initState = initState != null ? initState.merge(varControlFlowState.initState) : varControlFlowState.initState;
|
||||
if (!varControlFlowState.isDeclared) {
|
||||
isDeclared = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (initState == null) {
|
||||
throw new AssertionError("An empty set of incoming edges data");
|
||||
}
|
||||
enterInstructionData.put(variable, VariableControlFlowState.create(initState, isDeclared));
|
||||
}
|
||||
return enterInstructionData;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<VariableDescriptor, VariableControlFlowState> addVariableInitStateFromCurrentInstructionIfAny(
|
||||
@NotNull Instruction instruction,
|
||||
@NotNull Map<VariableDescriptor, VariableControlFlowState> enterInstructionData,
|
||||
@NotNull LexicalScopeVariableInfo lexicalScopeVariableInfo
|
||||
) {
|
||||
if (!(instruction instanceof WriteValueInstruction) && !(instruction instanceof VariableDeclarationInstruction)) {
|
||||
return enterInstructionData;
|
||||
}
|
||||
VariableDescriptor variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, bindingContext);
|
||||
if (variable == null) {
|
||||
return enterInstructionData;
|
||||
}
|
||||
Map<VariableDescriptor, VariableControlFlowState> exitInstructionData = Maps.newHashMap(enterInstructionData);
|
||||
if (instruction instanceof WriteValueInstruction) {
|
||||
// if writing to already initialized object
|
||||
if (!PseudocodeUtil.isThisOrNoDispatchReceiver((WriteValueInstruction) instruction, bindingContext)) {
|
||||
return enterInstructionData;
|
||||
}
|
||||
|
||||
VariableControlFlowState enterInitState = enterInstructionData.get(variable);
|
||||
VariableControlFlowState initializationAtThisElement =
|
||||
VariableControlFlowState
|
||||
.create(((WriteValueInstruction) instruction).getElement() instanceof KtProperty, enterInitState);
|
||||
exitInstructionData.put(variable, initializationAtThisElement);
|
||||
}
|
||||
else { // instruction instanceof VariableDeclarationInstruction
|
||||
VariableControlFlowState enterInitState = enterInstructionData.get(variable);
|
||||
if (enterInitState == null) {
|
||||
enterInitState = getDefaultValueForInitializers(variable, instruction, lexicalScopeVariableInfo);
|
||||
}
|
||||
if (enterInitState == null || !enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
|
||||
boolean isInitialized = enterInitState != null && enterInitState.mayBeInitialized();
|
||||
VariableControlFlowState variableDeclarationInfo = VariableControlFlowState.create(isInitialized, true);
|
||||
exitInstructionData.put(variable, variableDeclarationInfo);
|
||||
}
|
||||
}
|
||||
return exitInstructionData;
|
||||
}
|
||||
|
||||
// variable use
|
||||
|
||||
@NotNull
|
||||
public Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> getVariableUseStatusData() {
|
||||
return pseudocodeVariableDataCollector.collectData(
|
||||
BACKWARD, /*mergeDataWithLocalDeclarations=*/ true,
|
||||
new InstructionDataMergeStrategy<VariableUseState>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Edges<Map<VariableDescriptor, VariableUseState>> invoke(
|
||||
@NotNull Instruction instruction,
|
||||
@NotNull Collection<? extends Map<VariableDescriptor, VariableUseState>> incomingEdgesData
|
||||
) {
|
||||
|
||||
Map<VariableDescriptor, VariableUseState> enterResult = Maps.newHashMap();
|
||||
for (Map<VariableDescriptor, VariableUseState> edgeData : incomingEdgesData) {
|
||||
for (Map.Entry<VariableDescriptor, VariableUseState> entry : edgeData.entrySet()) {
|
||||
VariableDescriptor variableDescriptor = entry.getKey();
|
||||
VariableUseState variableUseState = entry.getValue();
|
||||
enterResult.put(variableDescriptor, variableUseState.merge(enterResult.get(variableDescriptor)));
|
||||
}
|
||||
}
|
||||
VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(
|
||||
instruction, true, bindingContext);
|
||||
if (variableDescriptor == null ||
|
||||
(!(instruction instanceof ReadValueInstruction) && !(instruction instanceof WriteValueInstruction))) {
|
||||
return new Edges<Map<VariableDescriptor, VariableUseState>>(enterResult, enterResult);
|
||||
}
|
||||
Map<VariableDescriptor, VariableUseState> exitResult = Maps.newHashMap(enterResult);
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
exitResult.put(variableDescriptor, VariableUseState.READ);
|
||||
}
|
||||
else { //instruction instanceof WriteValueInstruction
|
||||
VariableUseState variableUseState = enterResult.get(variableDescriptor);
|
||||
if (variableUseState == null) {
|
||||
variableUseState = VariableUseState.UNUSED;
|
||||
}
|
||||
switch (variableUseState) {
|
||||
case UNUSED:
|
||||
case ONLY_WRITTEN_NEVER_READ:
|
||||
exitResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ);
|
||||
break;
|
||||
case WRITTEN_AFTER_READ:
|
||||
case READ:
|
||||
exitResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ);
|
||||
}
|
||||
}
|
||||
return new Edges<Map<VariableDescriptor, VariableUseState>>(enterResult, exitResult);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private enum TriInitState {
|
||||
INITIALIZED("I"), UNKNOWN("I?"), NOT_INITIALIZED("");
|
||||
|
||||
private final String s;
|
||||
|
||||
TriInitState(String s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
private TriInitState merge(@NotNull TriInitState other) {
|
||||
if (this == other) return this;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VariableControlFlowState {
|
||||
|
||||
public final TriInitState initState;
|
||||
public final boolean isDeclared;
|
||||
|
||||
private VariableControlFlowState(TriInitState initState, boolean isDeclared) {
|
||||
this.initState = initState;
|
||||
this.isDeclared = isDeclared;
|
||||
}
|
||||
|
||||
private static final VariableControlFlowState VS_IT = new VariableControlFlowState(TriInitState.INITIALIZED, true);
|
||||
private static final VariableControlFlowState VS_IF = new VariableControlFlowState(TriInitState.INITIALIZED, false);
|
||||
private static final VariableControlFlowState VS_UT = new VariableControlFlowState(TriInitState.UNKNOWN, true);
|
||||
private static final VariableControlFlowState VS_UF = new VariableControlFlowState(TriInitState.UNKNOWN, false);
|
||||
private static final VariableControlFlowState VS_NT = new VariableControlFlowState(TriInitState.NOT_INITIALIZED, true);
|
||||
private static final VariableControlFlowState VS_NF = new VariableControlFlowState(TriInitState.NOT_INITIALIZED, false);
|
||||
|
||||
|
||||
private static VariableControlFlowState create(TriInitState initState, boolean isDeclared) {
|
||||
switch (initState) {
|
||||
case INITIALIZED: return isDeclared ? VS_IT : VS_IF;
|
||||
case UNKNOWN: return isDeclared ? VS_UT : VS_UF;
|
||||
default: return isDeclared ? VS_NT : VS_NF;
|
||||
}
|
||||
}
|
||||
|
||||
private static VariableControlFlowState create(boolean isInitialized, boolean isDeclared) {
|
||||
return create(isInitialized ? TriInitState.INITIALIZED : TriInitState.NOT_INITIALIZED, isDeclared);
|
||||
}
|
||||
|
||||
private static VariableControlFlowState create(boolean isInitialized) {
|
||||
return create(isInitialized, false);
|
||||
}
|
||||
|
||||
private static VariableControlFlowState create(boolean isDeclaredHere, @Nullable VariableControlFlowState mergedEdgesData) {
|
||||
return create(true, isDeclaredHere || (mergedEdgesData != null && mergedEdgesData.isDeclared));
|
||||
}
|
||||
|
||||
public boolean definitelyInitialized() {
|
||||
return initState == TriInitState.INITIALIZED;
|
||||
}
|
||||
|
||||
public boolean mayBeInitialized() {
|
||||
return initState != TriInitState.NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (initState == TriInitState.NOT_INITIALIZED && !isDeclared) return "-";
|
||||
return initState + (isDeclared ? "D" : "");
|
||||
}
|
||||
}
|
||||
|
||||
public enum VariableUseState {
|
||||
READ(3),
|
||||
WRITTEN_AFTER_READ(2),
|
||||
ONLY_WRITTEN_NEVER_READ(1),
|
||||
UNUSED(0);
|
||||
|
||||
private final int priority;
|
||||
|
||||
VariableUseState(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
private VariableUseState merge(@Nullable VariableUseState variableUseState) {
|
||||
if (variableUseState == null || priority > variableUseState.priority) return this;
|
||||
return variableUseState;
|
||||
}
|
||||
|
||||
public static boolean isUsed(@Nullable VariableUseState variableUseState) {
|
||||
return variableUseState != null && variableUseState != UNUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cfg
|
||||
|
||||
import com.google.common.collect.Maps
|
||||
import com.google.common.collect.Sets
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import java.util.Collections
|
||||
|
||||
class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingContext: BindingContext) {
|
||||
private val pseudocodeVariableDataCollector: PseudocodeVariableDataCollector
|
||||
|
||||
private val declaredVariablesForDeclaration = Maps.newHashMap<Pseudocode, Set<VariableDescriptor>>()
|
||||
|
||||
val variableInitializers: Map<Instruction, Edges<InitControlFlowInfo>> by lazy {
|
||||
computeVariableInitializers()
|
||||
}
|
||||
|
||||
init {
|
||||
this.pseudocodeVariableDataCollector = PseudocodeVariableDataCollector(bindingContext, pseudocode)
|
||||
}
|
||||
|
||||
val lexicalScopeVariableInfo: LexicalScopeVariableInfo
|
||||
get() = pseudocodeVariableDataCollector.lexicalScopeVariableInfo
|
||||
|
||||
fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> {
|
||||
if (!includeInsideLocalDeclarations) {
|
||||
return getUpperLevelDeclaredVariables(pseudocode)
|
||||
}
|
||||
val declaredVariables = Sets.newHashSet<VariableDescriptor>()
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(pseudocode))
|
||||
|
||||
for (localFunctionDeclarationInstruction in pseudocode.localDeclarations) {
|
||||
val localPseudocode = localFunctionDeclarationInstruction.body
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(localPseudocode))
|
||||
}
|
||||
return declaredVariables
|
||||
}
|
||||
|
||||
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
||||
var declaredVariables = declaredVariablesForDeclaration[pseudocode]
|
||||
if (declaredVariables == null) {
|
||||
declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode)
|
||||
declaredVariablesForDeclaration.put(pseudocode, declaredVariables)
|
||||
}
|
||||
return declaredVariables
|
||||
}
|
||||
|
||||
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
||||
val declaredVariables = Sets.newHashSet<VariableDescriptor>()
|
||||
for (instruction in pseudocode.instructions) {
|
||||
if (instruction is VariableDeclarationInstruction) {
|
||||
val variableDeclarationElement = instruction.variableDeclarationElement
|
||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
|
||||
if (descriptor != null) {
|
||||
assert(descriptor is VariableDescriptor)
|
||||
declaredVariables.add(descriptor as VariableDescriptor?)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(declaredVariables)
|
||||
}
|
||||
|
||||
// variable initializers
|
||||
|
||||
private fun computeVariableInitializers(): Map<Instruction, Edges<InitControlFlowInfo>> {
|
||||
|
||||
val lexicalScopeVariableInfo = pseudocodeVariableDataCollector.lexicalScopeVariableInfo
|
||||
|
||||
return pseudocodeVariableDataCollector.collectData(
|
||||
TraversalOrder.FORWARD, /*mergeDataWithLocalDeclarations=*/ true, InitControlFlowInfo()
|
||||
) {
|
||||
instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
|
||||
|
||||
val enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData)
|
||||
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
|
||||
instruction, enterInstructionData, lexicalScopeVariableInfo)
|
||||
Edges(enterInstructionData, exitInstructionData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addVariableInitStateFromCurrentInstructionIfAny(
|
||||
instruction: Instruction,
|
||||
enterInstructionData: InitControlFlowInfo,
|
||||
lexicalScopeVariableInfo: LexicalScopeVariableInfo): InitControlFlowInfo {
|
||||
if (instruction is MagicInstruction) {
|
||||
if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
|
||||
val exitInstructionData = enterInstructionData.copy()
|
||||
for (entry in enterInstructionData.entries) {
|
||||
if (!entry.value.definitelyInitialized()) {
|
||||
exitInstructionData.put(entry.key,
|
||||
VariableControlFlowState.createInitializedExhaustively(entry.value.isDeclared))
|
||||
}
|
||||
}
|
||||
return exitInstructionData
|
||||
}
|
||||
}
|
||||
if (instruction !is WriteValueInstruction && instruction !is VariableDeclarationInstruction) {
|
||||
return enterInstructionData
|
||||
}
|
||||
val variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, bindingContext) ?: return enterInstructionData
|
||||
val exitInstructionData = enterInstructionData.copy()
|
||||
if (instruction is WriteValueInstruction) {
|
||||
// if writing to already initialized object
|
||||
if (!PseudocodeUtil.isThisOrNoDispatchReceiver(instruction, bindingContext)) {
|
||||
return enterInstructionData
|
||||
}
|
||||
|
||||
val enterInitState = enterInstructionData[variable]
|
||||
val initializationAtThisElement = VariableControlFlowState.create(instruction.element is KtProperty, enterInitState)
|
||||
exitInstructionData.put(variable, initializationAtThisElement)
|
||||
}
|
||||
else {
|
||||
// instruction instanceof VariableDeclarationInstruction
|
||||
var enterInitState: VariableControlFlowState? = enterInstructionData[variable]
|
||||
if (enterInitState == null) {
|
||||
enterInitState = getDefaultValueForInitializers(variable, instruction, lexicalScopeVariableInfo)
|
||||
}
|
||||
if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
|
||||
val isInitialized = enterInitState.mayBeInitialized()
|
||||
val variableDeclarationInfo = VariableControlFlowState.create(isInitialized, true)
|
||||
exitInstructionData.put(variable, variableDeclarationInfo)
|
||||
}
|
||||
}
|
||||
return exitInstructionData
|
||||
}
|
||||
|
||||
// variable use
|
||||
|
||||
val variableUseStatusData: Map<Instruction, Edges<UseControlFlowInfo>>
|
||||
get() = pseudocodeVariableDataCollector.collectData(
|
||||
TraversalOrder.BACKWARD, true, UseControlFlowInfo()
|
||||
) {
|
||||
instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
||||
val enterResult = UseControlFlowInfo()
|
||||
for (edgeData in incomingEdgesData) {
|
||||
for (entry in edgeData.entries) {
|
||||
val variableDescriptor = entry.key
|
||||
val variableUseState = entry.value
|
||||
enterResult.put(variableDescriptor, variableUseState.merge(enterResult[variableDescriptor]))
|
||||
}
|
||||
}
|
||||
val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, bindingContext)
|
||||
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
||||
Edges(enterResult, enterResult)
|
||||
}
|
||||
else {
|
||||
val exitResult = enterResult.copy()
|
||||
if (instruction is ReadValueInstruction) {
|
||||
exitResult.put(variableDescriptor, VariableUseState.READ)
|
||||
}
|
||||
else {
|
||||
var variableUseState: VariableUseState? = enterResult[variableDescriptor]
|
||||
if (variableUseState == null) {
|
||||
variableUseState = VariableUseState.UNUSED
|
||||
}
|
||||
when (variableUseState) {
|
||||
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
|
||||
exitResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
|
||||
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
|
||||
exitResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
|
||||
}
|
||||
}
|
||||
Edges(enterResult, exitResult)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun getDefaultValueForInitializers(
|
||||
variable: VariableDescriptor,
|
||||
instruction: Instruction,
|
||||
lexicalScopeVariableInfo: LexicalScopeVariableInfo
|
||||
): VariableControlFlowState {
|
||||
//todo: think of replacing it with "MapWithDefaultValue"
|
||||
val declaredIn = lexicalScopeVariableInfo.declaredIn[variable]
|
||||
val declaredOutsideThisDeclaration =
|
||||
declaredIn == null //declared outside this pseudocode
|
||||
|| declaredIn.lexicalScopeForContainingDeclaration != instruction.lexicalScope.lexicalScopeForContainingDeclaration
|
||||
return VariableControlFlowState.create(/*initState=*/declaredOutsideThisDeclaration)
|
||||
}
|
||||
|
||||
private fun mergeIncomingEdgesDataForInitializers(
|
||||
incomingEdgesData: Collection<InitControlFlowInfo>
|
||||
): InitControlFlowInfo {
|
||||
val variablesInScope = Sets.newHashSet<VariableDescriptor>()
|
||||
for (edgeData in incomingEdgesData) {
|
||||
variablesInScope.addAll(edgeData.keys)
|
||||
}
|
||||
|
||||
val enterInstructionData = InitControlFlowInfo()
|
||||
for (variable in variablesInScope) {
|
||||
var initState: InitState? = null
|
||||
var isDeclared = true
|
||||
for (edgeData in incomingEdgesData) {
|
||||
val varControlFlowState = edgeData[variable]
|
||||
if (varControlFlowState != null) {
|
||||
initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState
|
||||
if (!varControlFlowState.isDeclared) {
|
||||
isDeclared = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (initState == null) {
|
||||
throw AssertionError("An empty set of incoming edges data")
|
||||
}
|
||||
enterInstructionData.put(variable, VariableControlFlowState.create(initState, isDeclared))
|
||||
}
|
||||
return enterInstructionData
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,10 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ThrowExceptionInst
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.MarkInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
|
||||
public class TailRecursionDetector extends InstructionVisitorWithResult<Boolean> implements Function1<Instruction, Boolean> {
|
||||
public class TailRecursionDetector extends InstructionVisitorWithResult<Boolean> implements Function1<Instruction, TraverseInstructionResult> {
|
||||
private final KtElement subroutine;
|
||||
private final Instruction start;
|
||||
|
||||
@@ -39,8 +40,8 @@ public class TailRecursionDetector extends InstructionVisitorWithResult<Boolean>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean invoke(@NotNull Instruction instruction) {
|
||||
return instruction == start || instruction.accept(this);
|
||||
public TraverseInstructionResult invoke(@NotNull Instruction instruction) {
|
||||
return instruction == start || instruction.accept(this) ? TraverseInstructionResult.CONTINUE : TraverseInstructionResult.HALT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.cfg;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
@@ -285,6 +286,6 @@ public final class WhenChecker {
|
||||
}
|
||||
|
||||
public static void checkReservedPrefix(@NotNull BindingTrace trace, @NotNull KtWhenExpression expression) {
|
||||
KtPsiUtilKt.checkReservedPrefixWord(trace, expression.getWhenKeyword(), "sealed", "sealed when");
|
||||
KtPsiUtilKt.checkReservedPrefixWord(trace, expression.getWhenKeyword(), "sealed", TokenSet.EMPTY, "sealed when");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@ import com.google.common.collect.*;
|
||||
import com.intellij.util.containers.BidirectionalMap;
|
||||
import kotlin.MapsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cfg.Label;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.*;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MergeInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction;
|
||||
@@ -33,6 +36,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterI
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction;
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.PseudocodeTraverserKt;
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
|
||||
import java.util.*;
|
||||
@@ -421,7 +425,17 @@ public class PseudocodeImpl implements Pseudocode {
|
||||
|
||||
private Set<Instruction> collectReachableInstructions() {
|
||||
Set<Instruction> visited = Sets.newHashSet();
|
||||
PseudocodeTraverserKt.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null);
|
||||
PseudocodeTraverserKt.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD,
|
||||
new Function1<Instruction, TraverseInstructionResult>() {
|
||||
@Override
|
||||
public TraverseInstructionResult invoke(Instruction instruction) {
|
||||
if (instruction instanceof MagicInstruction &&
|
||||
((MagicInstruction) instruction).getKind() == MagicKind.EXHAUSTIVE_WHEN_ELSE) {
|
||||
return TraverseInstructionResult.SKIP;
|
||||
}
|
||||
return TraverseInstructionResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
if (!visited.contains(getExitInstruction())) {
|
||||
visited.add(getExitInstruction());
|
||||
}
|
||||
|
||||
@@ -143,7 +143,8 @@ public enum class MagicKind(val sideEffectFree: Boolean = false) {
|
||||
UNRESOLVED_CALL(),
|
||||
UNSUPPORTED_ELEMENT(),
|
||||
UNRECOGNIZED_WRITE_RHS(),
|
||||
FAKE_INITIALIZER()
|
||||
FAKE_INITIALIZER(),
|
||||
EXHAUSTIVE_WHEN_ELSE()
|
||||
}
|
||||
|
||||
// Merges values produced by alternative control-flow paths (such as 'if' branches)
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
|
||||
@@ -112,7 +113,7 @@ public fun getExpectedTypePredicate(
|
||||
resolutionCandidate,
|
||||
DelegatingBindingTrace(bindingContext, "Compute type predicates for unresolved call arguments"),
|
||||
TracingStrategy.EMPTY,
|
||||
DataFlowInfoForArgumentsImpl(call)
|
||||
DataFlowInfoForArgumentsImpl(DataFlowInfo.EMPTY, call)
|
||||
)
|
||||
val status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(call,
|
||||
TracingStrategy.EMPTY,
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.KtReferenceExpression;
|
||||
import org.jetbrains.kotlin.psi.KtWhenExpression;
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
|
||||
@@ -165,6 +166,11 @@ public class CheckerTestUtil {
|
||||
debugAnnotations.add(new DebugInfoDiagnostic(expression, DebugInfoDiagnosticFactory.CONSTANT));
|
||||
}
|
||||
}
|
||||
for (KtWhenExpression expression : bindingContext.getSliceContents(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN).keySet()) {
|
||||
if (PsiTreeUtil.isAncestor(root, expression, false)) {
|
||||
debugAnnotations.add(new DebugInfoDiagnostic(expression, DebugInfoDiagnosticFactory.IMPLICIT_EXHAUSTIVE));
|
||||
}
|
||||
}
|
||||
return debugAnnotations;
|
||||
}
|
||||
|
||||
@@ -527,6 +533,7 @@ public class CheckerTestUtil {
|
||||
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 IMPLICIT_EXHAUSTIVE = new DebugInfoDiagnosticFactory("IMPLICIT_EXHAUSTIVE");
|
||||
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");
|
||||
|
||||
@@ -420,6 +420,8 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory0<KtParameter> USELESS_VARARG_ON_PARAMETER = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<KtDeclaration> MULTIPLE_VARARG_PARAMETERS = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
// Named parameters
|
||||
|
||||
DiagnosticFactory0<KtParameter> DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE = DiagnosticFactory0.create(ERROR, PARAMETER_DEFAULT_VALUE);
|
||||
@@ -590,8 +592,8 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> UNDERSCORE_IS_RESERVED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, String> INVALID_CHARACTERS = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_OPERATOR_MODIFIER = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_INFIX_MODIFIER = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, String> INAPPLICABLE_OPERATOR_MODIFIER = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_INFIX_MODIFIER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> OPERATOR_MODIFIER_REQUIRED = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory2<KtOperationReferenceExpression, FunctionDescriptor, String> INFIX_MODIFIER_REQUIRED = DiagnosticFactory2.create(ERROR);
|
||||
@@ -644,6 +646,7 @@ public interface Errors {
|
||||
// Nullability
|
||||
|
||||
DiagnosticFactory1<PsiElement, KotlinType> UNSAFE_CALL = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> UNSAFE_IMPLICIT_INVOKE_CALL = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory3<KtExpression, String, String, String> UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
|
||||
@@ -726,6 +729,8 @@ public interface Errors {
|
||||
DiagnosticFactory2<KtElement, KotlinType, KotlinType> INCOMPATIBLE_TYPES = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> IMPLICIT_NOTHING_RETURN_TYPE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> IMPLICIT_NOTHING_PROPERTY_TYPE = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> IMPLICIT_INTERSECTION_TYPE = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
// Context tracking
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(SETTER_VISIBILITY_DIFFERS_FROM_LATEINIT_VISIBILITY, "Setter visibility must be the same as lateinit property visibility");
|
||||
MAP.put(SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY, "Setter visibility must be the same or less permissive than property visibility");
|
||||
MAP.put(PRIVATE_SETTER_FOR_ABSTRACT_PROPERTY, "Private setters are not allowed for abstract properties");
|
||||
MAP.put(PRIVATE_SETTER_FOR_OPEN_PROPERTY, "Private setters are deprecated for open properties");
|
||||
MAP.put(PRIVATE_SETTER_FOR_OPEN_PROPERTY, "Private setters are not allowed for open properties");
|
||||
MAP.put(BACKING_FIELD_IN_INTERFACE, "Property in an interface cannot have a backing field");
|
||||
MAP.put(MUST_BE_INITIALIZED, "Property must be initialized");
|
||||
MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract");
|
||||
@@ -234,6 +234,7 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE, "An anonymous function is not allowed to specify default values for its parameters");
|
||||
MAP.put(USELESS_VARARG_ON_PARAMETER, "Vararg on this parameter is useless");
|
||||
MAP.put(MULTIPLE_VARARG_PARAMETERS, "Declarations with multiple vararg-parameters are prohibited");
|
||||
|
||||
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
|
||||
MAP.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here");
|
||||
@@ -362,7 +363,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(UNDERSCORE_IS_RESERVED, "Names _, __, ___, ..., are reserved in Kotlin");
|
||||
MAP.put(INVALID_CHARACTERS, "Name {0}", STRING);
|
||||
|
||||
MAP.put(INAPPLICABLE_OPERATOR_MODIFIER, "'operator' modifier is inapplicable on this function");
|
||||
MAP.put(INAPPLICABLE_OPERATOR_MODIFIER, "'operator' modifier is inapplicable on this function: {0}", STRING);
|
||||
MAP.put(INAPPLICABLE_INFIX_MODIFIER, "'infix' modifier is inapplicable on this function");
|
||||
|
||||
MAP.put(OPERATOR_MODIFIER_REQUIRED, "'operator' modifier is required on ''{0}'' in ''{1}''", NAME, STRING);
|
||||
@@ -469,6 +470,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(DYNAMIC_SUPERTYPE, "A supertype cannot be dynamic");
|
||||
MAP.put(REDUNDANT_NULLABLE, "Redundant '?'");
|
||||
MAP.put(UNSAFE_CALL, "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type {0}", RENDER_TYPE);
|
||||
MAP.put(UNSAFE_IMPLICIT_INVOKE_CALL, "Reference has a nullable type {0}, use explicit '?.invoke()' to make function-like call instead", RENDER_TYPE);
|
||||
MAP.put(AMBIGUOUS_LABEL, "Ambiguous label");
|
||||
MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING);
|
||||
MAP.put(EXCEPTION_FROM_ANALYZER, "Internal Error occurred while analyzing this expression:\n{0}", THROWABLE);
|
||||
@@ -527,6 +529,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(TYPE_MISMATCH_IN_CONDITION, "Condition must be of type kotlin.Boolean, but is of type {0}", RENDER_TYPE);
|
||||
MAP.put(INCOMPATIBLE_TYPES, "Incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE);
|
||||
MAP.put(IMPLICIT_NOTHING_RETURN_TYPE, "''Nothing'' return type needs to be specified explicitly");
|
||||
MAP.put(IMPLICIT_NOTHING_PROPERTY_TYPE, "Deprecated: ''Nothing'' property type needs to be specified explicitly");
|
||||
MAP.put(IMPLICIT_INTERSECTION_TYPE, "Inferred type {0} is an intersection, please specify the required type explicitly", RENDER_TYPE);
|
||||
MAP.put(EXPECTED_CONDITION, "Expected condition of type kotlin.Boolean");
|
||||
|
||||
MAP.put(CANNOT_CHECK_FOR_ERASED, "Cannot check for instance of erased type: {0}", RENDER_TYPE);
|
||||
|
||||
@@ -95,6 +95,7 @@ public object Renderers {
|
||||
|
||||
public val RENDER_CLASS_OR_OBJECT_NAME: Renderer<ClassDescriptor> = Renderer { it.renderKindWithName() }
|
||||
|
||||
@JvmField
|
||||
public val RENDER_TYPE: Renderer<KotlinType> = Renderer { DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) }
|
||||
|
||||
public val RENDER_POSITION_VARIANCE: Renderer<Variance> = Renderer {
|
||||
|
||||
@@ -241,11 +241,15 @@ public interface KtTokens {
|
||||
TokenSet OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, DOT, PLUSPLUS, MINUSMINUS, EXCLEXCL, MUL, PLUS,
|
||||
MINUS, EXCL, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR,
|
||||
SAFE_ACCESS, ELVIS,
|
||||
// MAP, FILTER,
|
||||
RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ,
|
||||
NOT_IN, NOT_IS,
|
||||
IDENTIFIER);
|
||||
|
||||
TokenSet BINARY_OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, MUL, PLUS,
|
||||
MINUS, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR,
|
||||
ELVIS, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ,
|
||||
NOT_IN, NOT_IS);
|
||||
|
||||
TokenSet AUGMENTED_ASSIGNMENTS = TokenSet.create(PLUSEQ, MINUSEQ, MULTEQ, PERCEQ, DIVEQ);
|
||||
TokenSet ALL_ASSIGNMENTS = TokenSet.create(EQ, PLUSEQ, MINUSEQ, MULTEQ, PERCEQ, DIVEQ);
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing {
|
||||
else if (at(DO_KEYWORD)) {
|
||||
parseDoWhile();
|
||||
}
|
||||
else if (atSet(CLASS_KEYWORD, FUN_KEYWORD, VAL_KEYWORD,
|
||||
else if (atSet(CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD, VAL_KEYWORD,
|
||||
VAR_KEYWORD, TYPE_ALIAS_KEYWORD)) {
|
||||
parseLocalDeclaration();
|
||||
}
|
||||
|
||||
@@ -143,4 +143,9 @@ public class KtPropertyAccessor extends KtDeclarationStub<KotlinPropertyAccessor
|
||||
public boolean hasInitializer() {
|
||||
return getInitializer() != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KtProperty getProperty() {
|
||||
return (KtProperty) getParent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.codeInsight.CommentUtilCore;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -608,9 +609,21 @@ public class KtPsiUtil {
|
||||
return prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example:
|
||||
* code: async* {}
|
||||
* element = "{}"
|
||||
* word = "async"
|
||||
* suffixTokens = [+, -, *, /, %]
|
||||
*
|
||||
* result = async
|
||||
*/
|
||||
@Nullable
|
||||
public static PsiElement getPreviousWord(@NotNull PsiElement element, @NotNull String word) {
|
||||
public static PsiElement getPreviousWord(@NotNull PsiElement element, @NotNull String word, @NotNull TokenSet suffixTokens) {
|
||||
PsiElement prev = prevLeafIgnoringWhitespaceAndComments(element);
|
||||
if (prev != null && suffixTokens.contains(prev.getNode().getElementType())) {
|
||||
prev = PsiTreeUtil.prevLeaf(prev, false);
|
||||
}
|
||||
if (prev != null && prev.getNode().getElementType() == KtTokens.IDENTIFIER && word.equals(prev.getText())) {
|
||||
return prev;
|
||||
}
|
||||
@@ -804,6 +817,9 @@ public class KtPsiUtil {
|
||||
if (current instanceof KtBlockExpression || current instanceof KtParameter) {
|
||||
return (KtElement) current;
|
||||
}
|
||||
if (current instanceof KtValueArgument) {
|
||||
return (KtElement) current;
|
||||
}
|
||||
|
||||
current = parent;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiParameter
|
||||
import com.intellij.psi.PsiParameterList
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
@@ -434,8 +435,8 @@ fun canPlaceAfterSimpleNameEntry(element: PsiElement?): Boolean {
|
||||
return !BAD_NEIGHBOUR_FOR_SIMPLE_TEMPLATE_ENTRY_PATTERN.matches(entryText)
|
||||
}
|
||||
|
||||
fun checkReservedPrefixWord(sink: DiagnosticSink, element: PsiElement, word: String, message: String) {
|
||||
KtPsiUtil.getPreviousWord(element, word)?.let {
|
||||
fun checkReservedPrefixWord(sink: DiagnosticSink, element: PsiElement, word: String, suffixTokens: TokenSet, message: String) {
|
||||
KtPsiUtil.getPreviousWord(element, word, suffixTokens)?.let {
|
||||
sink.report(Errors.UNSUPPORTED.on(it, message))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.BaseImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
|
||||
class AllUnderImportsScope(descriptor: DeclarationDescriptor) : BaseImportingScope(null) {
|
||||
class AllUnderImportScope(
|
||||
descriptor: DeclarationDescriptor,
|
||||
aliasImportNames: Collection<FqName>
|
||||
) : BaseImportingScope(null) {
|
||||
|
||||
private val scopes: List<ResolutionScope> = if (descriptor is ClassDescriptor) {
|
||||
listOf(descriptor.staticScope, descriptor.unsubstitutedInnerClassesScope)
|
||||
}
|
||||
@@ -37,17 +40,39 @@ class AllUnderImportsScope(descriptor: DeclarationDescriptor) : BaseImportingSco
|
||||
listOf((descriptor as PackageViewDescriptor).memberScope)
|
||||
}
|
||||
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean)
|
||||
= scopes.flatMap { it.getContributedDescriptors(kindFilter, nameFilter) }
|
||||
private val excludedNames = if (aliasImportNames.isEmpty()) { // optimization
|
||||
emptyList<Name>()
|
||||
}
|
||||
else {
|
||||
val fqName = DescriptorUtils.getFqNameSafe(descriptor)
|
||||
aliasImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }
|
||||
}
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation)
|
||||
= scopes.asSequence().mapNotNull { it.getContributedClassifier(name, location) }.singleOrNull()
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): List<DeclarationDescriptor> {
|
||||
val nameFilterToUse = if (excludedNames.isEmpty()) { // optimization
|
||||
nameFilter
|
||||
}
|
||||
else {
|
||||
{ it !in excludedNames && nameFilter(it) }
|
||||
}
|
||||
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation)
|
||||
= scopes.flatMap { it.getContributedVariables(name, location) }
|
||||
return scopes.flatMap { it.getContributedDescriptors(kindFilter, nameFilterToUse) }
|
||||
}
|
||||
|
||||
override fun getContributedFunctions(name: Name, location: LookupLocation)
|
||||
= scopes.flatMap { it.getContributedFunctions(name, location) }
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
if (name in excludedNames) return null
|
||||
return scopes.asSequence().mapNotNull { it.getContributedClassifier(name, location) }.singleOrNull()
|
||||
}
|
||||
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation): List<VariableDescriptor> {
|
||||
if (name in excludedNames) return emptyList()
|
||||
return scopes.flatMap { it.getContributedVariables(name, location) }
|
||||
}
|
||||
|
||||
override fun getContributedFunctions(name: Name, location: LookupLocation): List<FunctionDescriptor> {
|
||||
if (name in excludedNames) return emptyList()
|
||||
return scopes.flatMap { it.getContributedFunctions(name, location) }
|
||||
}
|
||||
|
||||
override fun printStructure(p: Printer) {
|
||||
p.println(javaClass.simpleName)
|
||||
@@ -24,6 +24,8 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import org.jetbrains.kotlin.resolve.constants.ErrorValue
|
||||
|
||||
public fun DeclarationDescriptor.hasJvmStaticAnnotation(): Boolean {
|
||||
return getAnnotations().findAnnotation(FqName("kotlin.jvm.JvmStatic")) != null
|
||||
@@ -51,8 +53,13 @@ private fun CallableDescriptor.isPlatformStaticIn(predicate: (DeclarationDescrip
|
||||
else -> predicate(getContainingDeclaration()) && hasJvmStaticAnnotation()
|
||||
}
|
||||
|
||||
public fun AnnotationDescriptor.argumentValue(parameterName: String): Any? {
|
||||
return getAllValueArguments().entrySet()
|
||||
.singleOrNull { it.key.getName().asString() == parameterName }
|
||||
?.value?.value
|
||||
fun AnnotationDescriptor.argumentValue(parameterName: String): Any? {
|
||||
val constant: ConstantValue<*>? = allValueArguments.entries
|
||||
.singleOrNull { it.key.name.asString() == parameterName }
|
||||
?.value
|
||||
|
||||
if (constant == null || constant is ErrorValue)
|
||||
return null
|
||||
|
||||
return constant.value
|
||||
}
|
||||
@@ -135,6 +135,7 @@ public interface BindingContext {
|
||||
WritableSlice<KtExpression, KotlinType> IMPLICIT_RECEIVER_SMARTCAST = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice<KtWhenExpression, Boolean> EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
||||
WritableSlice<KtWhenExpression, Boolean> IMPLICIT_EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice<KtElement, LexicalScope> LEXICAL_SCOPE = Slices.createSimpleSlice();
|
||||
|
||||
|
||||
@@ -48,6 +48,16 @@ public class BindingContextUtils {
|
||||
private BindingContextUtils() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VariableDescriptor extractVariableFromResolvedCall(
|
||||
@NotNull BindingContext bindingContext,
|
||||
@Nullable KtElement callElement
|
||||
) {
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(callElement, bindingContext);
|
||||
if (resolvedCall == null || !(resolvedCall.getResultingDescriptor() instanceof VariableDescriptor)) return null;
|
||||
return (VariableDescriptor) resolvedCall.getResultingDescriptor();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable KtElement element, boolean onlyReference) {
|
||||
DeclarationDescriptor descriptor = null;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext.TYPE
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.TYPE_PARAMETER
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveOpenMembers
|
||||
import org.jetbrains.kotlin.types.IntersectionTypeConstructor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SubstitutionUtils
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -66,12 +67,23 @@ fun KtTypeReference.checkNotEnumEntry(trace: BindingTrace): Boolean {
|
||||
return result
|
||||
}
|
||||
|
||||
internal class DeclarationsCheckerBuilder(
|
||||
private val descriptorResolver: DescriptorResolver,
|
||||
private val originalModifiersChecker: ModifiersChecker,
|
||||
private val annotationChecker: AnnotationChecker,
|
||||
private val identifierChecker: IdentifierChecker
|
||||
) {
|
||||
fun withTrace(trace: BindingTrace) =
|
||||
DeclarationsChecker(descriptorResolver, originalModifiersChecker, annotationChecker, identifierChecker, trace)
|
||||
}
|
||||
|
||||
class DeclarationsChecker(
|
||||
private val descriptorResolver: DescriptorResolver,
|
||||
modifiersChecker: ModifiersChecker,
|
||||
private val annotationChecker: AnnotationChecker,
|
||||
private val identifierChecker: IdentifierChecker,
|
||||
private val trace: BindingTrace) {
|
||||
private val trace: BindingTrace
|
||||
) {
|
||||
|
||||
private val modifiersChecker = modifiersChecker.withTrace(trace)
|
||||
|
||||
@@ -130,6 +142,7 @@ class DeclarationsChecker(
|
||||
declaration.checkTypeReferences()
|
||||
modifiersChecker.checkModifiersForDeclaration(declaration, constructorDescriptor)
|
||||
identifierChecker.checkDeclaration(declaration, trace)
|
||||
checkVarargParameters(declaration, constructorDescriptor)
|
||||
}
|
||||
|
||||
private fun checkModifiersAndAnnotationsInPackageDirective(file: KtFile) {
|
||||
@@ -323,11 +336,13 @@ class DeclarationsChecker(
|
||||
else if (aClass is KtEnumEntry) {
|
||||
checkEnumEntry(aClass, classDescriptor)
|
||||
}
|
||||
|
||||
for (memberDescriptor in classDescriptor.declaredCallableMembers) {
|
||||
if (memberDescriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) continue
|
||||
val member = DescriptorToSourceUtils.descriptorToDeclaration(memberDescriptor) as? KtFunction
|
||||
if (member != null && memberDescriptor is FunctionDescriptor) {
|
||||
checkFunctionExposedType(member, memberDescriptor)
|
||||
checkVarargParameters(member, memberDescriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,6 +446,7 @@ class DeclarationsChecker(
|
||||
checkTypeParameterConstraints(property)
|
||||
checkPropertyExposedType(property, propertyDescriptor)
|
||||
checkPropertyTypeParametersAreUsedInReceiverType(propertyDescriptor)
|
||||
checkImplicitCallableType(property, propertyDescriptor)
|
||||
}
|
||||
|
||||
private fun checkPropertyTypeParametersAreUsedInReceiverType(descriptor: PropertyDescriptor) {
|
||||
@@ -607,7 +623,7 @@ class DeclarationsChecker(
|
||||
checkMemberReceiverExposedType(property.receiverTypeReference, propertyDescriptor)
|
||||
}
|
||||
|
||||
private fun checkFunction(function: KtNamedFunction, functionDescriptor: SimpleFunctionDescriptor) {
|
||||
fun checkFunction(function: KtNamedFunction, functionDescriptor: SimpleFunctionDescriptor) {
|
||||
val typeParameterList = function.typeParameterList
|
||||
val nameIdentifier = function.nameIdentifier
|
||||
if (typeParameterList != null && nameIdentifier != null &&
|
||||
@@ -645,12 +661,32 @@ class DeclarationsChecker(
|
||||
if (!function.hasBody() && !hasAbstractModifier && !hasExternalModifier) {
|
||||
trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor))
|
||||
}
|
||||
functionDescriptor.returnType?.let {
|
||||
if (it.isNothing() && !function.hasDeclaredReturnType()) {
|
||||
trace.report(IMPLICIT_NOTHING_RETURN_TYPE.on(nameIdentifier ?: function))
|
||||
checkImplicitCallableType(function, functionDescriptor)
|
||||
checkFunctionExposedType(function, functionDescriptor)
|
||||
checkVarargParameters(function, functionDescriptor)
|
||||
}
|
||||
|
||||
private fun checkVarargParameters(declaration: KtDeclaration, callableDescriptor: CallableDescriptor) {
|
||||
val numberOfVarargParameters = callableDescriptor.valueParameters.count { it.varargElementType != null }
|
||||
if (numberOfVarargParameters > 1) {
|
||||
trace.report(MULTIPLE_VARARG_PARAMETERS.on(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkImplicitCallableType(declaration: KtCallableDeclaration, descriptor: CallableDescriptor) {
|
||||
descriptor.returnType?.let {
|
||||
if (declaration.typeReference == null) {
|
||||
val target = declaration.nameIdentifier ?: declaration
|
||||
if (it.isNothing()) {
|
||||
trace.report(
|
||||
(if (declaration is KtProperty) IMPLICIT_NOTHING_PROPERTY_TYPE else IMPLICIT_NOTHING_RETURN_TYPE).on(target)
|
||||
)
|
||||
}
|
||||
if (it.constructor is IntersectionTypeConstructor) {
|
||||
trace.report(IMPLICIT_INTERSECTION_TYPE.on(target, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
checkFunctionExposedType(function, functionDescriptor)
|
||||
}
|
||||
|
||||
private fun checkFunctionExposedType(function: KtFunction, functionDescriptor: FunctionDescriptor) {
|
||||
@@ -739,6 +775,20 @@ class DeclarationsChecker(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkVarargParameters(trace: BindingTrace, callableDescriptor: CallableDescriptor) {
|
||||
val numberOfVarargParameters = callableDescriptor.valueParameters.count { it.varargElementType != null }
|
||||
if (numberOfVarargParameters > 1) {
|
||||
for (parameter in callableDescriptor.valueParameters) {
|
||||
if (parameter.varargElementType != null) {
|
||||
val parameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(parameter)
|
||||
if (parameterDeclaration is KtParameter) {
|
||||
trace.report(MULTIPLE_VARARG_PARAMETERS.on(parameterDeclaration))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun removeDuplicateTypes(conflictingTypes: MutableSet<KotlinType>) {
|
||||
|
||||
@@ -20,9 +20,9 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import kotlin.CollectionsKt;
|
||||
import kotlin.Pair;
|
||||
import kotlin.SetsKt;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.collections.SetsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -42,22 +42,17 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
|
||||
import org.jetbrains.kotlin.resolve.scopes.ScopeUtils;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.ScopeUtils;
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
|
||||
import org.jetbrains.kotlin.storage.StorageManager;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
|
||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -75,31 +70,25 @@ public class DescriptorResolver {
|
||||
|
||||
@NotNull private final TypeResolver typeResolver;
|
||||
@NotNull private final AnnotationResolver annotationResolver;
|
||||
@NotNull private final ExpressionTypingServices expressionTypingServices;
|
||||
@NotNull private final DelegatedPropertyResolver delegatedPropertyResolver;
|
||||
@NotNull private final StorageManager storageManager;
|
||||
@NotNull private final KotlinBuiltIns builtIns;
|
||||
@NotNull private final ConstantExpressionEvaluator constantExpressionEvaluator;
|
||||
@NotNull private final SupertypeLoopChecker supertypeLoopsResolver;
|
||||
@NotNull private final VariableTypeResolver variableTypeResolver;
|
||||
|
||||
public DescriptorResolver(
|
||||
@NotNull AnnotationResolver annotationResolver,
|
||||
@NotNull KotlinBuiltIns builtIns,
|
||||
@NotNull DelegatedPropertyResolver delegatedPropertyResolver,
|
||||
@NotNull ExpressionTypingServices expressionTypingServices,
|
||||
@NotNull StorageManager storageManager,
|
||||
@NotNull TypeResolver typeResolver,
|
||||
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
||||
@NotNull SupertypeLoopChecker supertypeLoopsResolver
|
||||
@NotNull SupertypeLoopChecker supertypeLoopsResolver,
|
||||
@NotNull VariableTypeResolver variableTypeResolver
|
||||
) {
|
||||
this.annotationResolver = annotationResolver;
|
||||
this.builtIns = builtIns;
|
||||
this.delegatedPropertyResolver = delegatedPropertyResolver;
|
||||
this.expressionTypingServices = expressionTypingServices;
|
||||
this.storageManager = storageManager;
|
||||
this.typeResolver = typeResolver;
|
||||
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
||||
this.supertypeLoopsResolver = supertypeLoopsResolver;
|
||||
this.variableTypeResolver = variableTypeResolver;
|
||||
}
|
||||
|
||||
public List<KotlinType> resolveSupertypes(
|
||||
@@ -670,84 +659,6 @@ public class DescriptorResolver {
|
||||
return variableDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VariableDescriptor resolveLocalVariableDescriptor(
|
||||
LexicalScope scope,
|
||||
KtVariableDeclaration variable,
|
||||
DataFlowInfo dataFlowInfo,
|
||||
BindingTrace trace
|
||||
) {
|
||||
DeclarationDescriptor containingDeclaration = scope.getOwnerDescriptor();
|
||||
VariableDescriptor result;
|
||||
KotlinType type;
|
||||
if (KtPsiUtil.isScriptDeclaration(variable)) {
|
||||
PropertyDescriptorImpl propertyDescriptor = PropertyDescriptorImpl.create(
|
||||
containingDeclaration,
|
||||
annotationResolver.resolveAnnotationsWithArguments(scope, variable.getModifierList(), trace),
|
||||
Modality.FINAL,
|
||||
Visibilities.INTERNAL,
|
||||
variable.isVar(),
|
||||
KtPsiUtil.safeName(variable.getName()),
|
||||
CallableMemberDescriptor.Kind.DECLARATION,
|
||||
KotlinSourceElementKt.toSourceElement(variable),
|
||||
/* lateInit = */ false,
|
||||
/* isConst = */ false
|
||||
);
|
||||
// For a local variable the type must not be deferred
|
||||
type = getVariableType(propertyDescriptor, scope, variable, dataFlowInfo, false, trace);
|
||||
|
||||
ReceiverParameterDescriptor receiverParameter = ((ScriptDescriptor) containingDeclaration).getThisAsReceiverParameter();
|
||||
propertyDescriptor.setType(type, Collections.<TypeParameterDescriptor>emptyList(), receiverParameter, (KotlinType) null);
|
||||
initializeWithDefaultGetterSetter(propertyDescriptor);
|
||||
trace.record(BindingContext.VARIABLE, variable, propertyDescriptor);
|
||||
result = propertyDescriptor;
|
||||
}
|
||||
else {
|
||||
LocalVariableDescriptor variableDescriptor =
|
||||
resolveLocalVariableDescriptorWithType(scope, variable, null, trace);
|
||||
// For a local variable the type must not be deferred
|
||||
type = getVariableType(variableDescriptor, scope, variable, dataFlowInfo, false, trace);
|
||||
variableDescriptor.setOutType(type);
|
||||
result = variableDescriptor;
|
||||
}
|
||||
// Type annotations also should be resolved
|
||||
ForceResolveUtil.forceResolveAllContents(type.getAnnotations());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void initializeWithDefaultGetterSetter(PropertyDescriptorImpl propertyDescriptor) {
|
||||
PropertyGetterDescriptorImpl getter = propertyDescriptor.getGetter();
|
||||
if (getter == null && !Visibilities.isPrivate(propertyDescriptor.getVisibility())) {
|
||||
getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.Companion.getEMPTY());
|
||||
getter.initialize(propertyDescriptor.getType());
|
||||
}
|
||||
|
||||
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
|
||||
if (setter == null && propertyDescriptor.isVar()) {
|
||||
setter = DescriptorFactory.createDefaultSetter(propertyDescriptor, Annotations.Companion.getEMPTY());
|
||||
}
|
||||
propertyDescriptor.initialize(getter, setter);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LocalVariableDescriptor resolveLocalVariableDescriptorWithType(
|
||||
@NotNull LexicalScope scope,
|
||||
@NotNull KtVariableDeclaration variable,
|
||||
@Nullable KotlinType type,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
LocalVariableDescriptor variableDescriptor = new LocalVariableDescriptor(
|
||||
scope.getOwnerDescriptor(),
|
||||
annotationResolver.resolveAnnotationsWithArguments(scope, variable.getModifierList(), trace),
|
||||
KtPsiUtil.safeName(variable.getName()),
|
||||
type,
|
||||
variable.isVar(),
|
||||
KotlinSourceElementKt.toSourceElement(variable)
|
||||
);
|
||||
trace.record(BindingContext.VARIABLE, variable, variableDescriptor);
|
||||
return variableDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PropertyDescriptor resolvePropertyDescriptor(
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@@ -824,9 +735,10 @@ public class DescriptorResolver {
|
||||
ReceiverParameterDescriptor receiverDescriptor =
|
||||
DescriptorFactory.createExtensionReceiverParameterForCallable(propertyDescriptor, receiverType);
|
||||
|
||||
KotlinType type = getVariableType(propertyDescriptor,
|
||||
ScopeUtils.makeScopeForPropertyInitializer(scopeWithTypeParameters, propertyDescriptor),
|
||||
property, dataFlowInfo, true, trace);
|
||||
KotlinType type = variableTypeResolver.process(
|
||||
propertyDescriptor, ScopeUtils.makeScopeForPropertyInitializer(scopeWithTypeParameters, propertyDescriptor),
|
||||
property, dataFlowInfo, true, trace
|
||||
);
|
||||
|
||||
propertyDescriptor.setType(type, typeParameterDescriptors, getDispatchReceiverParameterIfNeeded(containingDeclaration),
|
||||
receiverDescriptor);
|
||||
@@ -858,127 +770,6 @@ public class DescriptorResolver {
|
||||
return hasBody;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private KotlinType getVariableType(
|
||||
@NotNull final VariableDescriptorWithInitializerImpl variableDescriptor,
|
||||
@NotNull final LexicalScope scopeForInitializer,
|
||||
@NotNull final KtVariableDeclaration variable,
|
||||
@NotNull final DataFlowInfo dataFlowInfo,
|
||||
boolean notLocal,
|
||||
@NotNull final BindingTrace trace
|
||||
) {
|
||||
KtTypeReference propertyTypeRef = variable.getTypeReference();
|
||||
|
||||
boolean hasDelegate = variable instanceof KtProperty && ((KtProperty) variable).hasDelegateExpression();
|
||||
if (propertyTypeRef == null) {
|
||||
if (!variable.hasInitializer()) {
|
||||
if (hasDelegate && variableDescriptor instanceof PropertyDescriptor) {
|
||||
final KtProperty property = (KtProperty) variable;
|
||||
if (property.hasDelegateExpression()) {
|
||||
return DeferredType.createRecursionIntolerant(
|
||||
storageManager,
|
||||
trace,
|
||||
new Function0<KotlinType>() {
|
||||
@Override
|
||||
public KotlinType invoke() {
|
||||
return resolveDelegatedPropertyType(property, (PropertyDescriptor) variableDescriptor, scopeForInitializer,
|
||||
property.getDelegateExpression(), dataFlowInfo, trace);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!notLocal) {
|
||||
trace.report(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.on(variable));
|
||||
}
|
||||
return ErrorUtils.createErrorType("No type, no body");
|
||||
}
|
||||
else {
|
||||
if (notLocal) {
|
||||
return DeferredType.createRecursionIntolerant(
|
||||
storageManager,
|
||||
trace,
|
||||
new Function0<KotlinType>() {
|
||||
@Override
|
||||
public KotlinType invoke() {
|
||||
PreliminaryDeclarationVisitor.Companion.createForDeclaration(variable, trace);
|
||||
KotlinType
|
||||
initializerType = resolveInitializerType(scopeForInitializer, variable.getInitializer(), dataFlowInfo, trace);
|
||||
setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace);
|
||||
return transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
KotlinType initializerType = resolveInitializerType(scopeForInitializer, variable.getInitializer(), dataFlowInfo, trace);
|
||||
setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace);
|
||||
return initializerType;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
KotlinType type = typeResolver.resolveType(scopeForInitializer, propertyTypeRef, trace, true);
|
||||
setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, type, trace);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
private void setConstantForVariableIfNeeded(
|
||||
@NotNull final VariableDescriptorWithInitializerImpl variableDescriptor,
|
||||
@NotNull final LexicalScope scope,
|
||||
@NotNull final KtVariableDeclaration variable,
|
||||
@NotNull final DataFlowInfo dataFlowInfo,
|
||||
@NotNull final KotlinType variableType,
|
||||
@NotNull final BindingTrace trace
|
||||
) {
|
||||
if (!shouldRecordInitializerForProperty(variableDescriptor, variableType)) return;
|
||||
|
||||
if (!variable.hasInitializer()) return;
|
||||
|
||||
variableDescriptor.setCompileTimeInitializer(
|
||||
storageManager.createRecursionTolerantNullableLazyValue(new Function0<ConstantValue<?>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public ConstantValue<?> invoke() {
|
||||
KtExpression initializer = variable.getInitializer();
|
||||
KotlinType initializerType = expressionTypingServices.safeGetType(scope, initializer, variableType, dataFlowInfo, trace);
|
||||
CompileTimeConstant<?> constant = constantExpressionEvaluator.evaluateExpression(initializer, trace, initializerType);
|
||||
|
||||
if (constant == null) return null;
|
||||
|
||||
if (constant.getUsesNonConstValAsConstant() && variableDescriptor.isConst()) {
|
||||
trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(initializer));
|
||||
}
|
||||
|
||||
return constant.toConstantValue(initializerType);
|
||||
}
|
||||
}, null)
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private KotlinType resolveDelegatedPropertyType(
|
||||
@NotNull KtProperty property,
|
||||
@NotNull PropertyDescriptor propertyDescriptor,
|
||||
@NotNull LexicalScope scopeForInitializer,
|
||||
@NotNull KtExpression delegateExpression,
|
||||
@NotNull DataFlowInfo dataFlowInfo,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
|
||||
KotlinType type = delegatedPropertyResolver.resolveDelegateExpression(
|
||||
delegateExpression, property, propertyDescriptor, scopeForInitializer, trace, dataFlowInfo);
|
||||
|
||||
if (type != null) {
|
||||
LexicalScope delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, propertyDescriptor);
|
||||
KotlinType getterReturnType = delegatedPropertyResolver
|
||||
.getDelegatedPropertyGetMethodReturnType(propertyDescriptor, delegateExpression, type, trace, delegateFunctionsScope);
|
||||
if (getterReturnType != null) {
|
||||
return getterReturnType;
|
||||
}
|
||||
}
|
||||
return ErrorUtils.createErrorType("Type from delegate");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/*package*/ static KotlinType transformAnonymousTypeIfNeeded(
|
||||
@@ -1005,15 +796,6 @@ public class DescriptorResolver {
|
||||
return type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private KotlinType resolveInitializerType(
|
||||
@NotNull LexicalScope scope,
|
||||
@NotNull KtExpression initializer,
|
||||
@NotNull DataFlowInfo dataFlowInfo,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
return expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PropertySetterDescriptor resolvePropertySetterDescriptor(
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.KtVariableDeclaration
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.source.toSourceElement
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.*
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
||||
|
||||
class LocalVariableResolver(
|
||||
private val expressionTypingServices: ExpressionTypingServices,
|
||||
private val modifiersChecker: ModifiersChecker,
|
||||
private val identifierChecker: IdentifierChecker,
|
||||
private val dataFlowAnalyzer: DataFlowAnalyzer,
|
||||
private val annotationResolver: AnnotationResolver,
|
||||
private val variableTypeResolver: VariableTypeResolver
|
||||
) {
|
||||
|
||||
fun process(
|
||||
property: KtProperty,
|
||||
typingContext: ExpressionTypingContext,
|
||||
scope: LexicalScope,
|
||||
facade: ExpressionTypingFacade
|
||||
): Pair<KotlinTypeInfo, VariableDescriptor> {
|
||||
val context = typingContext.replaceContextDependency(ContextDependency.INDEPENDENT).replaceScope(scope)
|
||||
val receiverTypeRef = property.receiverTypeReference
|
||||
if (receiverTypeRef != null) {
|
||||
context.trace.report(LOCAL_EXTENSION_PROPERTY.on(receiverTypeRef))
|
||||
}
|
||||
|
||||
val getter = property.getter
|
||||
if (getter != null) {
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_GETTER.on(getter))
|
||||
}
|
||||
|
||||
val setter = property.setter
|
||||
if (setter != null) {
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_SETTER.on(setter))
|
||||
}
|
||||
|
||||
val delegateExpression = property.delegateExpression
|
||||
if (delegateExpression != null) {
|
||||
expressionTypingServices.getTypeInfo(delegateExpression, context)
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_DELEGATE.on(property.delegate!!))
|
||||
}
|
||||
|
||||
val propertyDescriptor = resolveLocalVariableDescriptor(scope, property, context.dataFlowInfo, context.trace)
|
||||
val initializer = property.initializer
|
||||
var typeInfo: KotlinTypeInfo
|
||||
if (initializer != null) {
|
||||
val outType = propertyDescriptor.getType()
|
||||
typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType))
|
||||
val dataFlowInfo = typeInfo.dataFlowInfo
|
||||
val type = typeInfo.type
|
||||
// At this moment we do not take initializer value into account if type is given for a property
|
||||
// We can comment first part of this condition to take them into account, like here: var s: String? = "xyz"
|
||||
// In this case s will be not-nullable until it is changed
|
||||
if (property.typeReference == null && type != null) {
|
||||
val variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty(
|
||||
property, propertyDescriptor, context.trace.bindingContext,
|
||||
DescriptorUtils.getContainingModuleOrNull(scope.ownerDescriptor))
|
||||
val initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context)
|
||||
// We cannot say here anything new about initializerDataFlowValue
|
||||
// except it has the same value as variableDataFlowValue
|
||||
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue))
|
||||
}
|
||||
}
|
||||
else {
|
||||
typeInfo = noTypeInfo(context)
|
||||
}
|
||||
|
||||
ExpressionTypingUtils.checkVariableShadowing(context.scope, context.trace, propertyDescriptor)
|
||||
|
||||
property.checkTypeReferences(context.trace)
|
||||
modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(property, propertyDescriptor)
|
||||
identifierChecker.checkDeclaration(property, context.trace)
|
||||
return Pair(typeInfo.replaceType(dataFlowAnalyzer.checkStatementType(property, context)), propertyDescriptor)
|
||||
}
|
||||
|
||||
private fun resolveLocalVariableDescriptor(
|
||||
scope: LexicalScope,
|
||||
variable: KtVariableDeclaration,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
trace: BindingTrace
|
||||
): VariableDescriptor {
|
||||
val containingDeclaration = scope.ownerDescriptor
|
||||
val result: VariableDescriptor
|
||||
val type: KotlinType
|
||||
if (KtPsiUtil.isScriptDeclaration(variable)) {
|
||||
val propertyDescriptor = PropertyDescriptorImpl.create(
|
||||
containingDeclaration,
|
||||
annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace),
|
||||
Modality.FINAL,
|
||||
Visibilities.INTERNAL,
|
||||
variable.isVar,
|
||||
KtPsiUtil.safeName(variable.name),
|
||||
CallableMemberDescriptor.Kind.DECLARATION,
|
||||
variable.toSourceElement(),
|
||||
/* lateInit = */ false,
|
||||
/* isConst = */ false
|
||||
)
|
||||
// For a local variable the type must not be deferred
|
||||
type = variableTypeResolver.process(propertyDescriptor, scope, variable, dataFlowInfo, false, trace)
|
||||
|
||||
val receiverParameter = (containingDeclaration as ScriptDescriptor).thisAsReceiverParameter
|
||||
propertyDescriptor.setType(type, emptyList<TypeParameterDescriptor>(), receiverParameter, null as? KotlinType)
|
||||
initializeWithDefaultGetterSetter(propertyDescriptor)
|
||||
trace.record(BindingContext.VARIABLE, variable, propertyDescriptor)
|
||||
result = propertyDescriptor
|
||||
}
|
||||
else {
|
||||
val variableDescriptor = resolveLocalVariableDescriptorWithType(scope, variable, null, trace)
|
||||
// For a local variable the type must not be deferred
|
||||
type = variableTypeResolver.process(variableDescriptor, scope, variable, dataFlowInfo, false, trace)
|
||||
variableDescriptor.setOutType(type)
|
||||
result = variableDescriptor
|
||||
}
|
||||
// Type annotations also should be resolved
|
||||
ForceResolveUtil.forceResolveAllContents(type.annotations)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun initializeWithDefaultGetterSetter(propertyDescriptor: PropertyDescriptorImpl) {
|
||||
var getter = propertyDescriptor.getter
|
||||
if (getter == null && !Visibilities.isPrivate(propertyDescriptor.visibility)) {
|
||||
getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.EMPTY)
|
||||
getter.initialize(propertyDescriptor.type)
|
||||
}
|
||||
|
||||
var setter = propertyDescriptor.setter
|
||||
if (setter == null && propertyDescriptor.isVar) {
|
||||
setter = DescriptorFactory.createDefaultSetter(propertyDescriptor, Annotations.EMPTY)
|
||||
}
|
||||
propertyDescriptor.initialize(getter, setter)
|
||||
}
|
||||
|
||||
internal fun resolveLocalVariableDescriptorWithType(
|
||||
scope: LexicalScope,
|
||||
variable: KtVariableDeclaration,
|
||||
type: KotlinType?,
|
||||
trace: BindingTrace
|
||||
): LocalVariableDescriptor {
|
||||
val variableDescriptor = LocalVariableDescriptor(
|
||||
scope.ownerDescriptor,
|
||||
annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace),
|
||||
KtPsiUtil.safeName(variable.name),
|
||||
type,
|
||||
variable.isVar,
|
||||
variable.toSourceElement()
|
||||
)
|
||||
trace.record(BindingContext.VARIABLE, variable, variableDescriptor)
|
||||
return variableDescriptor
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.util.CheckResult
|
||||
import org.jetbrains.kotlin.util.OperatorChecks
|
||||
|
||||
public class OperatorModifierChecker : DeclarationChecker {
|
||||
@@ -51,8 +52,14 @@ public class OperatorModifierChecker : DeclarationChecker {
|
||||
if (!functionDescriptor.isOperator) return
|
||||
val modifier = declaration.modifierList?.getModifier(KtTokens.OPERATOR_KEYWORD) ?: return
|
||||
|
||||
if (!OperatorChecks.canBeOperator(functionDescriptor)) {
|
||||
diagnosticHolder.report(Errors.INAPPLICABLE_OPERATOR_MODIFIER.on(modifier))
|
||||
}
|
||||
val checkResult = OperatorChecks.checkOperator(functionDescriptor)
|
||||
if (checkResult.isSuccess) return
|
||||
|
||||
val errorDescription = if (checkResult is CheckResult.IllegalSignature)
|
||||
checkResult.error
|
||||
else
|
||||
"illegal function name"
|
||||
|
||||
diagnosticHolder.report(Errors.INAPPLICABLE_OPERATOR_MODIFIER.on(modifier, errorDescription))
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class OverloadResolver {
|
||||
else if (containingDeclaration instanceof ScriptDescriptor) {
|
||||
// TODO: check overload conflicts of functions with constructors in scripts
|
||||
}
|
||||
else if (!(containingDeclaration instanceof FunctionDescriptor)) {
|
||||
else if (!(containingDeclaration instanceof FunctionDescriptor || containingDeclaration instanceof PropertyDescriptor)) {
|
||||
throw new IllegalStateException("Illegal class container: " + containingDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
|
||||
@@ -133,6 +134,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
|
||||
importDirective: KtImportDirective,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
trace: BindingTrace,
|
||||
aliasImportNames: Collection<FqName>,
|
||||
packageFragmentForVisibilityCheck: PackageFragmentDescriptor?
|
||||
): ImportingScope? { // null if some error happened
|
||||
val importedReference = importDirective.importedReference ?: return null
|
||||
@@ -142,25 +144,33 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
|
||||
if (packageFragmentForVisibilityCheck is DeclarationDescriptorWithSource && packageFragmentForVisibilityCheck.source == SourceElement.NO_SOURCE) {
|
||||
PackageFragmentWithCustomSource(packageFragmentForVisibilityCheck, KotlinSourceElement(importDirective.getContainingKtFile()))
|
||||
}
|
||||
|
||||
else {
|
||||
packageFragmentForVisibilityCheck
|
||||
}
|
||||
|
||||
if (!importDirective.isAllUnder) {
|
||||
if (importDirective.isAllUnder) {
|
||||
val packageOrClassDescriptor = resolveToPackageOrClass(path, moduleDescriptor, trace, packageFragmentForCheck,
|
||||
scopeForFirstPart = null, position = QualifierPosition.IMPORT) ?: return null
|
||||
|
||||
if (packageOrClassDescriptor is ClassDescriptor && packageOrClassDescriptor.kind.isSingleton) {
|
||||
trace.report(Errors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON.on(lastPart.expression, packageOrClassDescriptor)) // todo report on star
|
||||
return null
|
||||
}
|
||||
|
||||
return AllUnderImportScope(packageOrClassDescriptor, aliasImportNames)
|
||||
}
|
||||
else {
|
||||
return processSingleImport(moduleDescriptor, trace, importDirective, path, lastPart, packageFragmentForCheck)
|
||||
}
|
||||
val packageOrClassDescriptor = resolveToPackageOrClass(path, moduleDescriptor, trace, packageFragmentForCheck,
|
||||
scopeForFirstPart = null, position = QualifierPosition.IMPORT) ?: return null
|
||||
if (packageOrClassDescriptor is ClassDescriptor && packageOrClassDescriptor.kind.isSingleton) {
|
||||
trace.report(Errors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON.on(lastPart.expression, packageOrClassDescriptor)) // todo report on star
|
||||
}
|
||||
return AllUnderImportsScope(packageOrClassDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processSingleImport(
|
||||
moduleDescriptor: ModuleDescriptor, trace: BindingTrace, importDirective: KtImportDirective,
|
||||
path: List<QualifierPart>, lastPart: QualifierPart, packageFragmentForVisibilityCheck: PackageFragmentDescriptor?
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
trace: BindingTrace,
|
||||
importDirective: KtImportDirective,
|
||||
path: List<QualifierPart>,
|
||||
lastPart: QualifierPart,
|
||||
packageFragmentForVisibilityCheck: PackageFragmentDescriptor?
|
||||
): SingleImportScope? {
|
||||
val aliasName = KtPsiUtil.getAliasName(importDirective)
|
||||
if (aliasName == null) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.PossiblyBareType.type
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver.FlexibleTypeCapabilitiesProvider
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.recordScope
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyEntity
|
||||
import org.jetbrains.kotlin.resolve.scopes.LazyScopeAdapter
|
||||
@@ -299,7 +300,7 @@ public class TypeResolver(
|
||||
|
||||
val projectionFromAllQualifierParts = qualifierResolutionResult.allProjections
|
||||
val parameters = typeConstructor.parameters
|
||||
if (c.allowBareTypes && projectionFromAllQualifierParts.isEmpty() && parameters.isNotEmpty()) {
|
||||
if (c.allowBareTypes && projectionFromAllQualifierParts.isEmpty() && isPossibleToSpecifyTypeArgumentsFor(classDescriptor)) {
|
||||
// See docs for PossiblyBareType
|
||||
return PossiblyBareType.bare(typeConstructor, false)
|
||||
}
|
||||
@@ -357,6 +358,24 @@ public class TypeResolver(
|
||||
return type(resultingType)
|
||||
}
|
||||
|
||||
// Returns true in case when at least one argument for this class could be specified
|
||||
// It could be always equal to 'typeConstructor.parameters.isNotEmpty()' unless local classes could captured type parameters
|
||||
// from enclosing functions. In such cases you can not specify any argument:
|
||||
// fun <E> foo(x: Any?) {
|
||||
// class C
|
||||
// if (x is C) { // 'C' should not be treated as bare type here
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// It's needed to determine whether this particular type could be bare
|
||||
private fun isPossibleToSpecifyTypeArgumentsFor(classDescriptor: ClassDescriptor): Boolean {
|
||||
// First parameter relates to the innermost declaration
|
||||
// If it's declared in function there
|
||||
val firstTypeParameter = classDescriptor.typeConstructor.parameters.firstOrNull() ?: return false
|
||||
return firstTypeParameter.original.containingDeclaration is ClassDescriptor
|
||||
}
|
||||
|
||||
private fun collectArgumentsForClassTypeConstructor(
|
||||
c: TypeResolutionContext,
|
||||
classDescriptor: ClassDescriptor,
|
||||
@@ -406,7 +425,9 @@ public class TypeResolver(
|
||||
|
||||
val parameters = classDescriptor.typeConstructor.parameters
|
||||
if (result.size < parameters.size) {
|
||||
if (parameters.subList(result.size, parameters.size).any { parameter -> !parameter.isDeclaredInScope(c) }) {
|
||||
val typeParametersToSpecify =
|
||||
parameters.subList(result.size, parameters.size).takeWhile { it.original.containingDeclaration is ClassDescriptor }
|
||||
if (typeParametersToSpecify.any { parameter -> !parameter.isDeclaredInScope(c) }) {
|
||||
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifierParts.last().expression, parameters.size))
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.VariableDescriptorWithInitializerImpl
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtVariableDeclaration
|
||||
import org.jetbrains.kotlin.resolve.DescriptorResolver.transformAnonymousTypeIfNeeded
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.ScopeUtils
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.DeferredType
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
|
||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
||||
|
||||
class VariableTypeResolver(
|
||||
private val storageManager: StorageManager,
|
||||
private val expressionTypingServices: ExpressionTypingServices,
|
||||
private val typeResolver: TypeResolver,
|
||||
private val constantExpressionEvaluator: ConstantExpressionEvaluator,
|
||||
private val delegatedPropertyResolver: DelegatedPropertyResolver
|
||||
) {
|
||||
|
||||
fun process(
|
||||
variableDescriptor: VariableDescriptorWithInitializerImpl,
|
||||
scopeForInitializer: LexicalScope,
|
||||
variable: KtVariableDeclaration,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
notLocal: Boolean,
|
||||
trace: BindingTrace
|
||||
): KotlinType {
|
||||
val propertyTypeRef = variable.typeReference
|
||||
|
||||
val hasDelegate = variable is KtProperty && variable.hasDelegateExpression()
|
||||
when {
|
||||
propertyTypeRef != null -> {
|
||||
val type = typeResolver.resolveType(scopeForInitializer, propertyTypeRef, trace, true)
|
||||
setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, type, trace)
|
||||
return type
|
||||
}
|
||||
!variable.hasInitializer() -> {
|
||||
if (hasDelegate && variableDescriptor is PropertyDescriptor) {
|
||||
val property = variable as KtProperty
|
||||
if (property.hasDelegateExpression()) {
|
||||
return DeferredType.createRecursionIntolerant(
|
||||
storageManager,
|
||||
trace
|
||||
) {
|
||||
resolveDelegatedPropertyType(property, variableDescriptor, scopeForInitializer,
|
||||
property.delegateExpression!!, dataFlowInfo, trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!notLocal) {
|
||||
trace.report(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.on(variable))
|
||||
}
|
||||
return ErrorUtils.createErrorType("No type, no body")
|
||||
}
|
||||
notLocal -> {
|
||||
return DeferredType.createRecursionIntolerant(
|
||||
storageManager,
|
||||
trace
|
||||
) {
|
||||
PreliminaryDeclarationVisitor.createForDeclaration(variable, trace)
|
||||
val initializerType = resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace)
|
||||
setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace)
|
||||
transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val initializerType = resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace)
|
||||
setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace)
|
||||
return initializerType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setConstantForVariableIfNeeded(
|
||||
variableDescriptor: VariableDescriptorWithInitializerImpl,
|
||||
scope: LexicalScope,
|
||||
variable: KtVariableDeclaration,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
variableType: KotlinType,
|
||||
trace: BindingTrace
|
||||
) {
|
||||
if (!DescriptorUtils.shouldRecordInitializerForProperty(variableDescriptor, variableType)) return
|
||||
|
||||
if (!variable.hasInitializer()) return
|
||||
|
||||
variableDescriptor.setCompileTimeInitializer(
|
||||
storageManager.createRecursionTolerantNullableLazyValue(
|
||||
{
|
||||
val initializer = variable.initializer
|
||||
val initializerType = expressionTypingServices.safeGetType(scope, initializer!!, variableType, dataFlowInfo, trace)
|
||||
val constant = constantExpressionEvaluator.evaluateExpression(initializer, trace, initializerType)
|
||||
?: return@createRecursionTolerantNullableLazyValue null
|
||||
|
||||
if (constant.usesNonConstValAsConstant && variableDescriptor.isConst) {
|
||||
trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(initializer))
|
||||
}
|
||||
|
||||
constant.toConstantValue(initializerType)
|
||||
},
|
||||
null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveDelegatedPropertyType(
|
||||
property: KtProperty,
|
||||
propertyDescriptor: PropertyDescriptor,
|
||||
scopeForInitializer: LexicalScope,
|
||||
delegateExpression: KtExpression,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
trace: BindingTrace
|
||||
): KotlinType {
|
||||
val type = delegatedPropertyResolver.resolveDelegateExpression(
|
||||
delegateExpression, property, propertyDescriptor, scopeForInitializer, trace, dataFlowInfo)
|
||||
|
||||
val delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, propertyDescriptor)
|
||||
val getterReturnType = delegatedPropertyResolver.getDelegatedPropertyGetMethodReturnType(propertyDescriptor, delegateExpression, type, trace, delegateFunctionsScope)
|
||||
if (getterReturnType != null) {
|
||||
return getterReturnType
|
||||
}
|
||||
return ErrorUtils.createErrorType("Type from delegate")
|
||||
}
|
||||
|
||||
private fun resolveInitializerType(
|
||||
scope: LexicalScope,
|
||||
initializer: KtExpression,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
trace: BindingTrace
|
||||
): KotlinType {
|
||||
return expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace)
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,6 @@ public class ArgumentTypeResolver {
|
||||
) {
|
||||
MutableDataFlowInfoForArguments infoForArguments = context.dataFlowInfoForArguments;
|
||||
Call call = context.call;
|
||||
infoForArguments.setInitialDataFlowInfo(context.dataFlowInfo);
|
||||
|
||||
for (ValueArgument argument : call.getValueArguments()) {
|
||||
KtExpression expression = argument.getArgumentExpression();
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
|
||||
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
||||
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DataFlowInfoForArgumentsImpl;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil;
|
||||
@@ -95,13 +96,15 @@ public class CallExpressionResolver {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ResolvedCall<FunctionDescriptor> getResolvedCallForFunction(
|
||||
private ResolvedCall<FunctionDescriptor> getResolvedCallForFunction(
|
||||
@NotNull Call call,
|
||||
@NotNull ResolutionContext context, @NotNull CheckArgumentTypesMode checkArguments,
|
||||
@NotNull boolean[] result
|
||||
@NotNull boolean[] result,
|
||||
@NotNull DataFlowInfo initialDataFlowInfoForArguments
|
||||
) {
|
||||
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
|
||||
BasicCallResolutionContext.create(context, call, checkArguments));
|
||||
BasicCallResolutionContext.create(context, call, checkArguments,
|
||||
new DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call)));
|
||||
if (!results.isNothing()) {
|
||||
result[0] = true;
|
||||
return OverloadResolutionResultsUtil.getResultingCall(results, context.contextDependency);
|
||||
@@ -145,6 +148,15 @@ public class CallExpressionResolver {
|
||||
public KotlinTypeInfo getSimpleNameExpressionTypeInfo(
|
||||
@NotNull KtSimpleNameExpression nameExpression, @Nullable Receiver receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
|
||||
) {
|
||||
return getSimpleNameExpressionTypeInfo(nameExpression, receiver, callOperationNode, context, context.dataFlowInfo);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private KotlinTypeInfo getSimpleNameExpressionTypeInfo(
|
||||
@NotNull KtSimpleNameExpression nameExpression, @Nullable Receiver receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context,
|
||||
@NotNull DataFlowInfo initialDataFlowInfoForArguments
|
||||
) {
|
||||
boolean[] result = new boolean[1];
|
||||
|
||||
@@ -155,7 +167,7 @@ public class CallExpressionResolver {
|
||||
|
||||
if (result[0]) {
|
||||
temporaryForVariable.commit();
|
||||
return TypeInfoFactoryKt.createTypeInfo(type, context);
|
||||
return TypeInfoFactoryKt.createTypeInfo(type, initialDataFlowInfoForArguments);
|
||||
}
|
||||
|
||||
Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
|
||||
@@ -163,7 +175,7 @@ public class CallExpressionResolver {
|
||||
context, "trace to resolve as function", nameExpression);
|
||||
ResolutionContext newContext = context.replaceTraceAndCache(temporaryForFunction);
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
|
||||
call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result);
|
||||
call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result, initialDataFlowInfoForArguments);
|
||||
if (result[0]) {
|
||||
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
|
||||
if (!(functionDescriptor instanceof ConstructorDescriptor)) {
|
||||
@@ -193,7 +205,8 @@ public class CallExpressionResolver {
|
||||
@NotNull KtCallExpression callExpression, @Nullable ReceiverValue receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
|
||||
) {
|
||||
KotlinTypeInfo typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(callExpression, receiver, callOperationNode, context);
|
||||
KotlinTypeInfo typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(
|
||||
callExpression, receiver, callOperationNode, context, context.dataFlowInfo);
|
||||
if (context.contextDependency == INDEPENDENT) {
|
||||
dataFlowAnalyzer.checkType(typeInfo.getType(), callExpression, context);
|
||||
}
|
||||
@@ -207,7 +220,8 @@ public class CallExpressionResolver {
|
||||
@NotNull
|
||||
private KotlinTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck(
|
||||
@NotNull KtCallExpression callExpression, @Nullable Receiver receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
|
||||
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context,
|
||||
@NotNull DataFlowInfo initialDataFlowInfoForArguments
|
||||
) {
|
||||
boolean[] result = new boolean[1];
|
||||
Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression);
|
||||
@@ -217,7 +231,9 @@ public class CallExpressionResolver {
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
|
||||
call,
|
||||
context.replaceTraceAndCache(temporaryForFunction),
|
||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result);
|
||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
|
||||
result,
|
||||
initialDataFlowInfoForArguments);
|
||||
if (result[0]) {
|
||||
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
|
||||
temporaryForFunction.commit();
|
||||
@@ -311,16 +327,19 @@ public class CallExpressionResolver {
|
||||
@NotNull Receiver receiver,
|
||||
@Nullable ASTNode callOperationNode,
|
||||
@Nullable KtExpression selectorExpression,
|
||||
@NotNull ExpressionTypingContext context
|
||||
@NotNull ExpressionTypingContext context,
|
||||
@NotNull DataFlowInfo initialDataFlowInfoForArguments
|
||||
) {
|
||||
if (selectorExpression instanceof KtCallExpression) {
|
||||
return getCallExpressionTypeInfoWithoutFinalTypeCheck((KtCallExpression) selectorExpression, receiver,
|
||||
callOperationNode, context);
|
||||
callOperationNode, context, initialDataFlowInfoForArguments);
|
||||
}
|
||||
else if (selectorExpression instanceof KtSimpleNameExpression) {
|
||||
return getSimpleNameExpressionTypeInfo((KtSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
|
||||
return getSimpleNameExpressionTypeInfo(
|
||||
(KtSimpleNameExpression) selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments);
|
||||
}
|
||||
else if (selectorExpression != null) {
|
||||
expressionTypingServices.getTypeInfo(selectorExpression, context);
|
||||
context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.getText()));
|
||||
}
|
||||
return TypeInfoFactoryKt.noTypeInfo(context);
|
||||
@@ -398,7 +417,6 @@ public class CallExpressionResolver {
|
||||
|
||||
boolean unconditional = true;
|
||||
DataFlowInfo unconditionalDataFlowInfo = receiverDataFlowInfo;
|
||||
ExpressionTypingContext contextForSelector = currentContext;
|
||||
|
||||
for (CallExpressionElement element : elementChain) {
|
||||
if (receiverType == null) {
|
||||
@@ -412,25 +430,25 @@ public class CallExpressionResolver {
|
||||
|
||||
boolean lastStage = element.getQualified() == expression;
|
||||
// Drop NO_EXPECTED_TYPE / INDEPENDENT at last stage
|
||||
ExpressionTypingContext baseContext = lastStage ? context : currentContext;
|
||||
ExpressionTypingContext contextForSelector = lastStage ? context : currentContext;
|
||||
if (TypeUtils.isNullableType(receiverType) && !element.getSafe()) {
|
||||
// Call with nullable receiver: take unconditional data flow info
|
||||
contextForSelector = baseContext.replaceDataFlowInfo(unconditionalDataFlowInfo);
|
||||
contextForSelector = contextForSelector.replaceDataFlowInfo(unconditionalDataFlowInfo);
|
||||
}
|
||||
else {
|
||||
// Take data flow info from the current receiver
|
||||
contextForSelector = baseContext.replaceDataFlowInfo(receiverDataFlowInfo);
|
||||
contextForSelector = contextForSelector.replaceDataFlowInfo(receiverDataFlowInfo);
|
||||
}
|
||||
|
||||
DataFlowInfo initialDataFlowInfoForArguments = contextForSelector.dataFlowInfo;
|
||||
if (receiver instanceof ReceiverValue) {
|
||||
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
|
||||
// Additional "receiver != null" information
|
||||
// Should be applied if we consider a safe call
|
||||
if (element.getSafe()) {
|
||||
DataFlowInfo dataFlowInfo = contextForSelector.dataFlowInfo;
|
||||
if (dataFlowInfo.getPredictableNullability(receiverDataFlowValue).canBeNull()) {
|
||||
contextForSelector = contextForSelector.replaceDataFlowInfo(
|
||||
dataFlowInfo.disequate(receiverDataFlowValue, DataFlowValue.nullValue(builtIns)));
|
||||
if (initialDataFlowInfoForArguments.getPredictableNullability(receiverDataFlowValue).canBeNull()) {
|
||||
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
|
||||
receiverDataFlowValue, DataFlowValue.nullValue(builtIns));
|
||||
}
|
||||
else {
|
||||
reportUnnecessarySafeCall(trace, receiverType, element.getNode(), receiver);
|
||||
@@ -439,8 +457,8 @@ public class CallExpressionResolver {
|
||||
}
|
||||
|
||||
KtExpression selectorExpression = element.getSelector();
|
||||
KotlinTypeInfo selectorReturnTypeInfo =
|
||||
getSelectorReturnTypeInfo(receiver, element.getNode(), selectorExpression, contextForSelector);
|
||||
KotlinTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
|
||||
receiver, element.getNode(), selectorExpression, contextForSelector, initialDataFlowInfoForArguments);
|
||||
KotlinType selectorReturnType = selectorReturnTypeInfo.getType();
|
||||
|
||||
if (qualifierReceiver != null) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedT
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isExplicitSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.SubstitutionFilteringInternalResolveAnnotations
|
||||
@@ -444,7 +445,6 @@ public class CandidateResolver(
|
||||
val candidateDescriptor = candidateCall.getCandidateDescriptor()
|
||||
if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS
|
||||
|
||||
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.getCall().isExplicitSafeCall()
|
||||
val isSubtypeBySmartCastIgnoringNullability = smartCastManager.isSubTypeBySmartCastIgnoringNullability(
|
||||
receiverArgument, receiverParameter.getType(), this)
|
||||
|
||||
@@ -455,12 +455,27 @@ public class CandidateResolver(
|
||||
|
||||
// Here we know that receiver is OK ignoring nullability and check that nullability is OK too
|
||||
// Doing it simply as full subtyping check (receiverValueType <: receiverParameterType)
|
||||
val call = candidateCall.call
|
||||
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall()
|
||||
val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type
|
||||
val smartCastNeeded = !ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, expectedReceiverParameterType)
|
||||
var reportUnsafeCall = false
|
||||
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this)
|
||||
val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue)
|
||||
var nullableImplicitInvokeReceiver = false
|
||||
var receiverArgumentType = receiverArgument.type
|
||||
if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) {
|
||||
val outerCallReceiver = call.outerCall.explicitReceiver
|
||||
if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) {
|
||||
val outerReceiverDataFlowValue = DataFlowValueFactory.createDataFlowValue(outerCallReceiver, this)
|
||||
val outerReceiverNullability = dataFlowInfo.getPredictableNullability(outerReceiverDataFlowValue)
|
||||
if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
||||
nullableImplicitInvokeReceiver = true
|
||||
receiverArgumentType = TypeUtils.makeNullable(receiverArgumentType)
|
||||
}
|
||||
}
|
||||
}
|
||||
val expression = (receiverArgument as? ExpressionReceiver)?.expression
|
||||
if (nullability.canBeNull() && !nullability.canBeNonNull()) {
|
||||
if (!TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
||||
@@ -470,7 +485,7 @@ public class CandidateResolver(
|
||||
expression?.let { trace.record(BindingContext.SMARTCAST_NULL, it) }
|
||||
}
|
||||
}
|
||||
else if (smartCastNeeded) {
|
||||
else if (!nullableImplicitInvokeReceiver && smartCastNeeded) {
|
||||
// Look if smart cast has some useful nullability info
|
||||
|
||||
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
|
||||
@@ -486,9 +501,7 @@ public class CandidateResolver(
|
||||
}
|
||||
}
|
||||
|
||||
val receiverArgumentType = receiverArgument.type
|
||||
|
||||
if (reportUnsafeCall) {
|
||||
if (reportUnsafeCall || nullableImplicitInvokeReceiver) {
|
||||
tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck)
|
||||
return UNSAFE_CALL_ERROR
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
public final class CallCandidateResolutionContext<D extends CallableDescriptor> extends CallResolutionContext<CallCandidateResolutionContext<D>> {
|
||||
|
||||
@@ -60,10 +60,10 @@ public abstract class CallResolutionContext<Context extends CallResolutionContex
|
||||
this.dataFlowInfoForArguments = dataFlowInfoForArguments;
|
||||
}
|
||||
else if (checkArguments == CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS) {
|
||||
this.dataFlowInfoForArguments = new DataFlowInfoForArgumentsImpl(call);
|
||||
this.dataFlowInfoForArguments = new DataFlowInfoForArgumentsImpl(dataFlowInfo, call);
|
||||
}
|
||||
else {
|
||||
this.dataFlowInfoForArguments = new MutableDataFlowInfoForArguments.WithoutArgumentsCheck();
|
||||
this.dataFlowInfoForArguments = new MutableDataFlowInfoForArguments.WithoutArgumentsCheck(dataFlowInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,13 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DataFlowInfoForArgumentsImpl implements MutableDataFlowInfoForArguments {
|
||||
@NotNull private final Call call; //for better debug messages only
|
||||
public class DataFlowInfoForArgumentsImpl extends MutableDataFlowInfoForArguments {
|
||||
@Nullable private Map<ValueArgument, DataFlowInfo> infoMap = null;
|
||||
@Nullable private Map<ValueArgument, ValueArgument> nextArgument = null;
|
||||
@Nullable private DataFlowInfo initialInfo;
|
||||
@Nullable private DataFlowInfo resultInfo;
|
||||
|
||||
public DataFlowInfoForArgumentsImpl(@NotNull Call call) {
|
||||
this.call = call;
|
||||
public DataFlowInfoForArgumentsImpl(@NotNull DataFlowInfo initialInfo, @NotNull Call call) {
|
||||
super(initialInfo);
|
||||
initNextArgMap(call.getValueArguments());
|
||||
}
|
||||
|
||||
@@ -54,21 +52,14 @@ public class DataFlowInfoForArgumentsImpl implements MutableDataFlowInfoForArgum
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitialDataFlowInfo(@NotNull DataFlowInfo dataFlowInfo) {
|
||||
//TODO assert initialInfo == null
|
||||
initialInfo = dataFlowInfo;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataFlowInfo getInfo(@NotNull ValueArgument valueArgument) {
|
||||
assert initialInfo != null : "Initial data flow info was not set for call: " + call;
|
||||
DataFlowInfo infoForArgument = infoMap == null ? null : infoMap.get(valueArgument);
|
||||
if (infoForArgument == null) {
|
||||
return initialInfo;
|
||||
return initialDataFlowInfo;
|
||||
}
|
||||
return initialInfo.and(infoForArgument);
|
||||
return initialDataFlowInfo.and(infoForArgument);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,8 +79,7 @@ public class DataFlowInfoForArgumentsImpl implements MutableDataFlowInfoForArgum
|
||||
@NotNull
|
||||
@Override
|
||||
public DataFlowInfo getResultInfo() {
|
||||
assert initialInfo != null : "Initial data flow info was not set for call: " + call;
|
||||
if (resultInfo == null) return initialInfo;
|
||||
return initialInfo.and(resultInfo);
|
||||
if (resultInfo == null) return initialDataFlowInfo;
|
||||
return initialDataFlowInfo.and(resultInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,18 +20,26 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.psi.ValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
|
||||
public interface MutableDataFlowInfoForArguments extends DataFlowInfoForArguments {
|
||||
public abstract class MutableDataFlowInfoForArguments implements DataFlowInfoForArguments {
|
||||
|
||||
void setInitialDataFlowInfo(@NotNull DataFlowInfo dataFlowInfo);
|
||||
@NotNull protected final DataFlowInfo initialDataFlowInfo;
|
||||
|
||||
void updateInfo(@NotNull ValueArgument valueArgument, @NotNull DataFlowInfo dataFlowInfo);
|
||||
public MutableDataFlowInfoForArguments(@NotNull DataFlowInfo initialDataFlowInfo) {
|
||||
this.initialDataFlowInfo = initialDataFlowInfo;
|
||||
}
|
||||
|
||||
class WithoutArgumentsCheck implements MutableDataFlowInfoForArguments {
|
||||
private DataFlowInfo dataFlowInfo;
|
||||
public abstract void updateInfo(@NotNull ValueArgument valueArgument, @NotNull DataFlowInfo dataFlowInfo);
|
||||
|
||||
@Override
|
||||
public void setInitialDataFlowInfo(@NotNull DataFlowInfo dataFlowInfo) {
|
||||
this.dataFlowInfo = dataFlowInfo;
|
||||
@NotNull
|
||||
@Override
|
||||
public DataFlowInfo getResultInfo() {
|
||||
return initialDataFlowInfo;
|
||||
}
|
||||
|
||||
public static class WithoutArgumentsCheck extends MutableDataFlowInfoForArguments {
|
||||
|
||||
public WithoutArgumentsCheck(@NotNull DataFlowInfo dataFlowInfo) {
|
||||
super(dataFlowInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,11 +52,5 @@ public interface MutableDataFlowInfoForArguments extends DataFlowInfoForArgument
|
||||
public DataFlowInfo getInfo(@NotNull ValueArgument valueArgument) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataFlowInfo getResultInfo() {
|
||||
return dataFlowInfo;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,29 +49,16 @@ class CandidateCallWithArgumentMapping<D : CallableDescriptor, K> private constr
|
||||
|
||||
val isGeneric: Boolean = resolvedCall.resultingDescriptor.original.typeParameters.isNotEmpty()
|
||||
|
||||
private val upperBoundsSubstitutor =
|
||||
BoundsSubstitutor.createUpperBoundsSubstitutor(resolvedCall.resultingDescriptor)
|
||||
|
||||
fun getExtensionReceiverType(substituteUpperBounds: Boolean): KotlinType? =
|
||||
resultingDescriptor.extensionReceiverParameter?.type?.let {
|
||||
extensionReceiverType ->
|
||||
if (substituteUpperBounds)
|
||||
upperBoundsSubstitutor.substitute(extensionReceiverType, Variance.INVARIANT)
|
||||
else
|
||||
extensionReceiverType
|
||||
}
|
||||
val extensionReceiverType: KotlinType?
|
||||
get() = resultingDescriptor.extensionReceiverParameter?.type
|
||||
|
||||
/**
|
||||
* Returns the type of a value that can be used in place of the corresponding parameter.
|
||||
*/
|
||||
fun getValueParameterType(argumentKey: K, substituteUpperBounds: Boolean): KotlinType? =
|
||||
fun getValueParameterType(argumentKey: K): KotlinType? =
|
||||
argumentsToParameters[argumentKey]?.let {
|
||||
valueParameterDescriptor ->
|
||||
val valueParameterType = valueParameterDescriptor.varargElementType ?: valueParameterDescriptor.type
|
||||
if (substituteUpperBounds)
|
||||
upperBoundsSubstitutor.substitute(valueParameterType, Variance.INVARIANT)
|
||||
else
|
||||
valueParameterType
|
||||
valueParameterDescriptor.varargElementType ?: valueParameterDescriptor.type
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -24,43 +24,42 @@ import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.toHandle
|
||||
import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionMutableResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Specificity
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.getSpecificityRelationTo
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
|
||||
fun <D : CallableDescriptor> findMaximallySpecific(
|
||||
candidates: Set<MutableResolvedCall<D>>,
|
||||
discriminateGenericDescriptors: Boolean,
|
||||
checkArgumentsMode: CheckArgumentTypesMode
|
||||
checkArgumentsMode: CheckArgumentTypesMode,
|
||||
discriminateGenerics: Boolean
|
||||
): MutableResolvedCall<D>? =
|
||||
if (candidates.size <= 1)
|
||||
candidates.firstOrNull()
|
||||
else when (checkArgumentsMode) {
|
||||
CheckArgumentTypesMode.CHECK_CALLABLE_TYPE ->
|
||||
uniquifyCandidatesSet(candidates).filter {
|
||||
isMaxSpecific(it, candidates) {
|
||||
isDefinitelyMostSpecific(it, candidates) {
|
||||
call1, call2 ->
|
||||
isNotLessSpecificCallableReference(call1.resultingDescriptor, call2.resultingDescriptor)
|
||||
}
|
||||
}.singleOrNull()
|
||||
|
||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS ->
|
||||
findMaximallySpecificCall(candidates, discriminateGenericDescriptors)
|
||||
findMaximallySpecificCall(candidates, discriminateGenerics)
|
||||
}
|
||||
|
||||
fun <D : CallableDescriptor> findMaximallySpecificVariableAsFunctionCalls(
|
||||
candidates: Set<MutableResolvedCall<D>>,
|
||||
discriminateGenericDescriptors: Boolean
|
||||
): Set<MutableResolvedCall<D>> {
|
||||
fun <D : CallableDescriptor> findMaximallySpecificVariableAsFunctionCalls(candidates: Set<MutableResolvedCall<D>>): Set<MutableResolvedCall<D>> {
|
||||
val variableCalls = candidates.mapTo(newResolvedCallSet<MutableResolvedCall<VariableDescriptor>>(candidates.size)) {
|
||||
if (it is VariableAsFunctionMutableResolvedCall)
|
||||
it.variableCall
|
||||
@@ -68,7 +67,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
throw AssertionError("Regular call among variable-as-function calls: $it")
|
||||
}
|
||||
|
||||
val maxSpecificVariableCall = findMaximallySpecificCall(variableCalls, discriminateGenericDescriptors) ?: return emptySet()
|
||||
val maxSpecificVariableCall = findMaximallySpecificCall(variableCalls, false) ?: return emptySet()
|
||||
|
||||
return candidates.filterTo(newResolvedCallSet<MutableResolvedCall<D>>(2)) {
|
||||
it.resultingVariableDescriptor == maxSpecificVariableCall.resultingDescriptor
|
||||
@@ -77,7 +76,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
|
||||
private fun <D : CallableDescriptor> findMaximallySpecificCall(
|
||||
candidates: Set<MutableResolvedCall<D>>,
|
||||
discriminateGenericDescriptors: Boolean
|
||||
discriminateGenerics: Boolean
|
||||
): MutableResolvedCall<D>? {
|
||||
val filteredCandidates = uniquifyCandidatesSet(candidates)
|
||||
|
||||
@@ -88,37 +87,41 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
CandidateCallWithArgumentMapping.create(candidateCall) { it.arguments.filter { it.getArgumentExpression() != null } }
|
||||
}
|
||||
|
||||
val (varargCandidates, regularCandidates) = conflictingCandidates.partition { it.resultingDescriptor.hasVarargs }
|
||||
val mostSpecificRegularCandidates = regularCandidates.selectMostSpecificCallsWithArgumentMapping(discriminateGenericDescriptors)
|
||||
|
||||
return when {
|
||||
mostSpecificRegularCandidates.size > 1 ->
|
||||
null
|
||||
mostSpecificRegularCandidates.size == 1 ->
|
||||
mostSpecificRegularCandidates.single()
|
||||
else ->
|
||||
varargCandidates.selectMostSpecificCallsWithArgumentMapping(discriminateGenericDescriptors).singleOrNull()
|
||||
val bestCandidatesByParameterTypes = conflictingCandidates.mapNotNull {
|
||||
candidate ->
|
||||
candidate.check {
|
||||
isMostSpecific(candidate, conflictingCandidates) {
|
||||
call1, call2 ->
|
||||
isNotLessSpecificCallWithArgumentMapping(call1, call2, discriminateGenerics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestCandidatesByParameterTypes.exactMaxWith { call1, call2 -> isOfNotLessSpecificShape(call1, call2) }?.resolvedCall
|
||||
}
|
||||
|
||||
private fun <D : CallableDescriptor, K> Collection<CandidateCallWithArgumentMapping<D, K>>.selectMostSpecificCallsWithArgumentMapping(
|
||||
discriminateGenericDescriptors: Boolean
|
||||
): Collection<MutableResolvedCall<D>> =
|
||||
this.mapNotNull {
|
||||
candidate ->
|
||||
candidate.check {
|
||||
isMaxSpecific(candidate, this) {
|
||||
call1, call2 ->
|
||||
isNotLessSpecificCallWithArgumentMapping(call1, call2, discriminateGenericDescriptors)
|
||||
}
|
||||
}?.resolvedCall
|
||||
private inline fun <C : Any> Collection<C>.exactMaxWith(isNotWorse: (C, C) -> Boolean): C? {
|
||||
var result: C? = null
|
||||
for (candidate in this) {
|
||||
if (result == null || isNotWorse(candidate, result)) {
|
||||
result = candidate
|
||||
}
|
||||
}
|
||||
if (result == null) return null
|
||||
if (any { it != result && isNotWorse(it, result!!) }) {
|
||||
return null
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun <C> isMostSpecific(candidate: C, candidates: Collection<C>, isNotLessSpecific: (C, C) -> Boolean): Boolean =
|
||||
candidates.all {
|
||||
other ->
|
||||
candidate === other ||
|
||||
isNotLessSpecific(candidate, other)
|
||||
}
|
||||
|
||||
private inline fun <C> isMaxSpecific(
|
||||
candidate: C,
|
||||
candidates: Collection<C>,
|
||||
isNotLessSpecific: (C, C) -> Boolean
|
||||
): Boolean =
|
||||
private inline fun <C> isDefinitelyMostSpecific(candidate: C, candidates: Collection<C>, isNotLessSpecific: (C, C) -> Boolean): Boolean =
|
||||
candidates.all {
|
||||
other ->
|
||||
candidate === other ||
|
||||
@@ -131,33 +134,59 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
private fun <D : CallableDescriptor, K> isNotLessSpecificCallWithArgumentMapping(
|
||||
call1: CandidateCallWithArgumentMapping<D, K>,
|
||||
call2: CandidateCallWithArgumentMapping<D, K>,
|
||||
discriminateGenericDescriptors: Boolean
|
||||
discriminateGenerics: Boolean
|
||||
): Boolean {
|
||||
return tryCompareDescriptorsFromScripts(call1.resultingDescriptor, call2.resultingDescriptor) ?:
|
||||
compareCallsWithArgumentMapping(call1, call2, discriminateGenericDescriptors)
|
||||
compareCallsByUsedArguments(call1, call2, discriminateGenerics)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if `d1` is definitely not less specific than `d2`,
|
||||
* `false` if `d1` is definitely less specific than `d2` or undecided.
|
||||
* `false` otherwise.
|
||||
*/
|
||||
private fun <D : CallableDescriptor, K> compareCallsWithArgumentMapping(
|
||||
private fun <D : CallableDescriptor, K> compareCallsByUsedArguments(
|
||||
call1: CandidateCallWithArgumentMapping<D, K>,
|
||||
call2: CandidateCallWithArgumentMapping<D, K>,
|
||||
discriminateGenericDescriptors: Boolean
|
||||
discriminateGenerics: Boolean
|
||||
): Boolean {
|
||||
val substituteParameterTypes =
|
||||
if (discriminateGenericDescriptors) {
|
||||
if (!call1.isGeneric && call2.isGeneric) return true
|
||||
if (call1.isGeneric && !call2.isGeneric) return false
|
||||
call1.isGeneric && call2.isGeneric
|
||||
}
|
||||
else false
|
||||
if (discriminateGenerics) {
|
||||
val isGeneric1 = call1.isGeneric
|
||||
val isGeneric2 = call2.isGeneric
|
||||
// generic loses to non-generic
|
||||
if (isGeneric1 && !isGeneric2) return false
|
||||
if (!isGeneric1 && isGeneric2) return true
|
||||
// two generics are non-comparable
|
||||
if (isGeneric1 && isGeneric2) return false
|
||||
}
|
||||
|
||||
val extensionReceiverType1 = call1.getExtensionReceiverType(substituteParameterTypes)
|
||||
val extensionReceiverType2 = call2.getExtensionReceiverType(substituteParameterTypes)
|
||||
tryCompareExtensionReceiverType(extensionReceiverType1, extensionReceiverType2)?.let {
|
||||
return it
|
||||
val typeParameters = call2.resolvedCall.resultingDescriptor.typeParameters
|
||||
val constraintSystemBuilder: ConstraintSystem.Builder = ConstraintSystemBuilderImpl()
|
||||
var hasConstraints = false
|
||||
val typeSubstitutor = constraintSystemBuilder.registerTypeVariables(call1.resolvedCall.call.toHandle(), typeParameters)
|
||||
|
||||
fun compareTypesAndUpdateConstraints(type1: KotlinType?, type2: KotlinType?, constraintPosition: ConstraintPosition): Boolean {
|
||||
if (type1 == null || type2 == null) return true
|
||||
|
||||
if (typeParameters.isEmpty() || !TypeUtils.dependsOnTypeParameters(type2, typeParameters)) {
|
||||
if (!typeNotLessSpecific(type1, type2)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
else if (isDefinitelyLessSpecificByTypeSpecificity(type1, type2)) {
|
||||
return false
|
||||
}
|
||||
else {
|
||||
hasConstraints = true
|
||||
val substitutedType2 = typeSubstitutor.safeSubstitute(type2, Variance.INVARIANT)
|
||||
constraintSystemBuilder.addSubtypeConstraint(type1, substitutedType2, constraintPosition)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
val extensionReceiverType1 = call1.extensionReceiverType
|
||||
val extensionReceiverType2 = call2.extensionReceiverType
|
||||
if (!compareTypesAndUpdateConstraints(extensionReceiverType1, extensionReceiverType2, RECEIVER_POSITION.position())) {
|
||||
return false
|
||||
}
|
||||
|
||||
assert(call1.argumentsCount == call2.argumentsCount) {
|
||||
@@ -165,14 +194,36 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
}
|
||||
|
||||
for (argumentKey in call1.argumentKeys) {
|
||||
val type1 = call1.getValueParameterType(argumentKey, substituteParameterTypes) ?: continue
|
||||
val type2 = call2.getValueParameterType(argumentKey, substituteParameterTypes) ?: continue
|
||||
val type1 = call1.getValueParameterType(argumentKey)
|
||||
val type2 = call2.getValueParameterType(argumentKey)
|
||||
|
||||
if (!typeNotLessSpecific(type1, type2)) {
|
||||
// We use this constraint system for subtyping relation check only,
|
||||
// so exact value parameter position doesn't matter.
|
||||
if (!compareTypesAndUpdateConstraints(type1, type2, VALUE_PARAMETER_POSITION.position(0))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConstraints) {
|
||||
constraintSystemBuilder.fixVariables()
|
||||
val constraintSystem = constraintSystemBuilder.build()
|
||||
if (constraintSystem.status.hasContradiction()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun <D: CallableDescriptor, K> isOfNotLessSpecificShape(
|
||||
call1: CandidateCallWithArgumentMapping<D, K>,
|
||||
call2: CandidateCallWithArgumentMapping<D, K>
|
||||
): Boolean {
|
||||
val hasVarargs1 = call1.resultingDescriptor.hasVarargs
|
||||
val hasVarargs2 = call2.resultingDescriptor.hasVarargs
|
||||
if (hasVarargs1 && !hasVarargs2) return false
|
||||
if (!hasVarargs1 && hasVarargs2) return true
|
||||
|
||||
if (call1.parametersWithDefaultValuesCount > call2.parametersWithDefaultValuesCount) {
|
||||
return false
|
||||
}
|
||||
@@ -264,16 +315,18 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
|
||||
if (!isSubtype) return false
|
||||
|
||||
val sThanG = specific.getSpecificityRelationTo(general)
|
||||
val gThanS = general.getSpecificityRelationTo(specific)
|
||||
if (sThanG == Specificity.Relation.LESS_SPECIFIC &&
|
||||
gThanS != Specificity.Relation.LESS_SPECIFIC) {
|
||||
return false
|
||||
}
|
||||
if (isDefinitelyLessSpecificByTypeSpecificity(specific, general)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isDefinitelyLessSpecificByTypeSpecificity(specific: KotlinType, general: KotlinType): Boolean {
|
||||
val sThanG = specific.getSpecificityRelationTo(general)
|
||||
val gThanS = general.getSpecificityRelationTo(specific)
|
||||
return sThanG == Specificity.Relation.LESS_SPECIFIC &&
|
||||
gThanS != Specificity.Relation.LESS_SPECIFIC
|
||||
}
|
||||
|
||||
private fun numericTypeMoreSpecific(specific: KotlinType, general: KotlinType): Boolean {
|
||||
val _double = builtIns.doubleType
|
||||
val _float = builtIns.floatType
|
||||
|
||||
@@ -195,7 +195,7 @@ public class ResolutionResultsHandler {
|
||||
}
|
||||
|
||||
if (candidates.iterator().next() instanceof VariableAsFunctionResolvedCall) {
|
||||
candidates = overloadingConflictResolver.findMaximallySpecificVariableAsFunctionCalls(candidates, discriminateGenerics);
|
||||
candidates = overloadingConflictResolver.findMaximallySpecificVariableAsFunctionCalls(candidates);
|
||||
}
|
||||
|
||||
Set<MutableResolvedCall<D>> noOverrides = OverrideResolver.filterOutOverridden(candidates, MAP_TO_RESULT);
|
||||
@@ -203,14 +203,14 @@ public class ResolutionResultsHandler {
|
||||
return OverloadResolutionResultsImpl.success(noOverrides.iterator().next());
|
||||
}
|
||||
|
||||
MutableResolvedCall<D> maximallySpecific = overloadingConflictResolver.findMaximallySpecific(noOverrides, false, checkArgumentsMode);
|
||||
MutableResolvedCall<D> maximallySpecific = overloadingConflictResolver.findMaximallySpecific(noOverrides, checkArgumentsMode, false);
|
||||
if (maximallySpecific != null) {
|
||||
return OverloadResolutionResultsImpl.success(maximallySpecific);
|
||||
}
|
||||
|
||||
if (discriminateGenerics) {
|
||||
MutableResolvedCall<D> maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific(
|
||||
noOverrides, true, checkArgumentsMode);
|
||||
noOverrides, checkArgumentsMode, true);
|
||||
if (maximallySpecificGenericsDiscriminated != null) {
|
||||
return OverloadResolutionResultsImpl.success(maximallySpecificGenericsDiscriminated);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
|
||||
|
||||
// We do not want to make methods static to keep SmartCastManager as a component
|
||||
@SuppressWarnings("MethodMayBeStatic")
|
||||
public class SmartCastManager {
|
||||
|
||||
@NotNull
|
||||
@@ -73,12 +75,12 @@ public class SmartCastManager {
|
||||
@NotNull DeclarationDescriptor containingDeclarationOrModule,
|
||||
@NotNull DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
final List<KotlinType> variants = getSmartCastVariants(receiverToCast, bindingContext,
|
||||
containingDeclarationOrModule, dataFlowInfo);
|
||||
final List<KotlinType> variants = CollectionsKt.distinct(
|
||||
getSmartCastVariants(receiverToCast, bindingContext, containingDeclarationOrModule, dataFlowInfo));
|
||||
return CollectionsKt.filter(variants, new Function1<KotlinType, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(final KotlinType type) {
|
||||
return !CollectionsKt.any(variants, new Function1<KotlinType, Boolean>() {
|
||||
return CollectionsKt.none(variants, new Function1<KotlinType, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(KotlinType another) {
|
||||
return another != type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type);
|
||||
|
||||
@@ -180,6 +180,9 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
|
||||
trace.report(UNSAFE_INFIX_CALL.on(reference, left.getText(), operationString.asString(), right.getText()));
|
||||
}
|
||||
}
|
||||
else if (isCallForImplicitInvoke) {
|
||||
trace.report(UNSAFE_IMPLICIT_INVOKE_CALL.on(reference, type));
|
||||
}
|
||||
else {
|
||||
trace.report(UNSAFE_CALL.on(reference, type));
|
||||
}
|
||||
|
||||
@@ -42,9 +42,6 @@ interface ScopeTower {
|
||||
val location: LookupLocation
|
||||
|
||||
val dataFlowInfo: DataFlowDecorator
|
||||
|
||||
// The closest (the most local) levels goes first
|
||||
val levels: List<ScopeTowerLevel>
|
||||
}
|
||||
|
||||
interface DataFlowDecorator {
|
||||
|
||||
@@ -51,37 +51,6 @@ internal class ScopeTowerImpl(
|
||||
|
||||
override val implicitReceivers = resolutionContext.scope.getImplicitReceiversHierarchy().
|
||||
mapNotNull { it.value.check { !it.type.containsError() } }
|
||||
|
||||
override val levels: List<ScopeTowerLevel> = createLevels()
|
||||
|
||||
private fun createLevels(): List<ScopeTowerLevel> {
|
||||
val result = ArrayList<ScopeTowerLevel>()
|
||||
|
||||
// locals win
|
||||
lexicalScope.parentsWithSelf.
|
||||
filterIsInstance<LexicalScope>().
|
||||
filter { it.kind.withLocalDescriptors }.
|
||||
mapTo(result) { ScopeBasedTowerLevel(this, it) }
|
||||
|
||||
var isFirstImportingScope = true
|
||||
lexicalScope.parentsWithSelf.forEach { scope ->
|
||||
if (scope is LexicalScope) {
|
||||
if (!scope.kind.withLocalDescriptors) result.add(ScopeBasedTowerLevel(this, scope))
|
||||
|
||||
scope.implicitReceiver?.let { result.add(ReceiverScopeTowerLevel(this, it.value)) }
|
||||
}
|
||||
else {
|
||||
if (isFirstImportingScope) {
|
||||
isFirstImportingScope = false
|
||||
result.add(SyntheticScopeBasedTowerLevel(this, syntheticScopes))
|
||||
}
|
||||
result.add(ImportingScopeBasedTowerLevel(this, scope as ImportingScope))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DataFlowDecoratorImpl(private val resolutionContext: ResolutionContext<*>): DataFlowDecorator {
|
||||
|
||||
@@ -21,13 +21,17 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasHidesMembersAnnotation
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasLowPriorityInOverloadResolution
|
||||
import org.jetbrains.kotlin.resolve.scopes.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.CastImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectVariables
|
||||
import org.jetbrains.kotlin.resolve.selectMostSpecificInEachOverridableGroup
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -184,6 +188,28 @@ internal class SyntheticScopeBasedTowerLevel(
|
||||
}
|
||||
}
|
||||
|
||||
internal class HidesMembersTowerLevel(scopeTower: ScopeTower): AbstractScopeTowerLevel(scopeTower) {
|
||||
override fun getVariables(name: Name, extensionReceiver: ReceiverValue?)
|
||||
= getCandidates(name, extensionReceiver, LexicalScope::collectVariables)
|
||||
|
||||
override fun getFunctions(name: Name, extensionReceiver: ReceiverValue?)
|
||||
= getCandidates(name, extensionReceiver, LexicalScope::collectFunctions)
|
||||
|
||||
private fun <T: CallableDescriptor> getCandidates(
|
||||
name: Name,
|
||||
extensionReceiver: ReceiverValue?,
|
||||
collectCandidates: LexicalScope.(Name, LookupLocation) -> Collection<T>
|
||||
): Collection<CandidateWithBoundDispatchReceiver<T>> {
|
||||
if (extensionReceiver == null || name !in HIDES_MEMBERS_NAME_LIST) return emptyList()
|
||||
|
||||
return scopeTower.lexicalScope.collectCandidates(name, location).filter {
|
||||
it.extensionReceiverParameter != null && it.hasHidesMembersAnnotation()
|
||||
}.map {
|
||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.getClassifierFromMeAndSuperclasses(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
var superclass: KotlinType? = this
|
||||
while (superclass != null) {
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.tower
|
||||
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import java.util.*
|
||||
|
||||
@@ -69,38 +73,106 @@ class TowerResolver {
|
||||
return run(context, processor, false, AllCandidatesCollector(context))
|
||||
}
|
||||
|
||||
private fun ScopeTower.createNonLocalLevels(): List<ScopeTowerLevel> {
|
||||
val result = ArrayList<ScopeTowerLevel>()
|
||||
|
||||
lexicalScope.parentsWithSelf.forEach { scope ->
|
||||
if (scope is LexicalScope) {
|
||||
if (!scope.kind.withLocalDescriptors) result.add(ScopeBasedTowerLevel(this, scope))
|
||||
|
||||
scope.implicitReceiver?.let { result.add(ReceiverScopeTowerLevel(this, it.value)) }
|
||||
}
|
||||
else {
|
||||
result.add(ImportingScopeBasedTowerLevel(this, scope as ImportingScope))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ScopeTower.createTowerDataList(): List<TowerData> = ArrayList<TowerData>().apply {
|
||||
operator fun TowerData.unaryPlus() = add(this)
|
||||
|
||||
val localLevels = lexicalScope.parentsWithSelf.
|
||||
filterIsInstance<LexicalScope>().filter { it.kind.withLocalDescriptors }.
|
||||
map { ScopeBasedTowerLevel(this@createTowerDataList, it) }
|
||||
|
||||
val nonLocalLevels = createNonLocalLevels()
|
||||
val hidesMembersLevel = HidesMembersTowerLevel(this@createTowerDataList)
|
||||
val syntheticLevel = SyntheticScopeBasedTowerLevel(this@createTowerDataList, syntheticScopes)
|
||||
|
||||
// hides members extensions for explicit receiver
|
||||
+ TowerData.TowerLevel(hidesMembersLevel)
|
||||
// possibly there is explicit member
|
||||
+ TowerData.Empty
|
||||
// synthetic member for explicit receiver
|
||||
+ TowerData.TowerLevel(syntheticLevel)
|
||||
|
||||
// local non-extensions or extension for explicit receiver
|
||||
for (localLevel in localLevels) {
|
||||
+ TowerData.TowerLevel(localLevel)
|
||||
}
|
||||
|
||||
for (scope in lexicalScope.parentsWithSelf) {
|
||||
if (scope is LexicalScope) {
|
||||
// statics
|
||||
if (!scope.kind.withLocalDescriptors) {
|
||||
+ TowerData.TowerLevel(ScopeBasedTowerLevel(this@createTowerDataList, scope))
|
||||
}
|
||||
|
||||
val implicitReceiver = scope.implicitReceiver?.value
|
||||
if (implicitReceiver != null) {
|
||||
// hides members extensions
|
||||
+ TowerData.BothTowerLevelAndImplicitReceiver(hidesMembersLevel, implicitReceiver)
|
||||
|
||||
// members of implicit receiver or member extension for explicit receiver
|
||||
+ TowerData.TowerLevel(ReceiverScopeTowerLevel(this@createTowerDataList, implicitReceiver))
|
||||
|
||||
// synthetic members
|
||||
+ TowerData.BothTowerLevelAndImplicitReceiver(syntheticLevel, implicitReceiver)
|
||||
|
||||
// invokeExtension on local variable
|
||||
+ TowerData.OnlyImplicitReceiver(implicitReceiver)
|
||||
|
||||
// local extensions for implicit receiver
|
||||
for (localLevel in localLevels) {
|
||||
+ TowerData.BothTowerLevelAndImplicitReceiver(localLevel, implicitReceiver)
|
||||
}
|
||||
|
||||
// extension for implicit receiver
|
||||
for (nonLocalLevel in nonLocalLevels) {
|
||||
+ TowerData.BothTowerLevelAndImplicitReceiver(nonLocalLevel, implicitReceiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// functions with no receiver or extension for explicit receiver
|
||||
+ TowerData.TowerLevel(ImportingScopeBasedTowerLevel(this@createTowerDataList, scope as ImportingScope))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <C> run(
|
||||
context: TowerContext<C>,
|
||||
processor: ScopeTowerProcessor<C>,
|
||||
useOrder: Boolean,
|
||||
resultCollector: ResultCollector<C>
|
||||
): Collection<C> {
|
||||
fun collectCandidates(data: TowerData): Collection<C>? {
|
||||
|
||||
for (towerData in context.scopeTower.createTowerDataList()) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
val candidatesGroups = if (useOrder) {
|
||||
processor.process(data)
|
||||
}
|
||||
else {
|
||||
listOf(processor.process(data).flatMap { it })
|
||||
}
|
||||
processor.process(towerData)
|
||||
}
|
||||
else {
|
||||
listOf(processor.process(towerData).flatMap { it })
|
||||
}
|
||||
|
||||
for (candidatesGroup in candidatesGroups) {
|
||||
resultCollector.pushCandidates(candidatesGroup)
|
||||
resultCollector.getSuccessfulCandidates()?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
for (implicitReceiver in context.scopeTower.implicitReceivers) {
|
||||
collectCandidates(TowerData.OnlyImplicitReceiver(implicitReceiver))?.let { return it }
|
||||
}
|
||||
// possible there is explicit member
|
||||
collectCandidates(TowerData.Empty)?.let { return it }
|
||||
|
||||
for (level in context.scopeTower.levels) {
|
||||
for (implicitReceiver in context.scopeTower.implicitReceivers) {
|
||||
collectCandidates(TowerData.BothTowerLevelAndImplicitReceiver(level, implicitReceiver))?.let { return it }
|
||||
}
|
||||
collectCandidates(TowerData.TowerLevel(level))?.let { return it }
|
||||
}
|
||||
|
||||
return resultCollector.getFinalCandidates()
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
|
||||
import org.jetbrains.kotlin.resolve.calls.CallTransformer
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
|
||||
// resolved call
|
||||
@@ -65,6 +66,11 @@ public fun <D : CallableDescriptor> ResolvedCall<D>.getParameterForArgument(valu
|
||||
return (valueArgument?.let { getArgumentMapping(it) } as? ArgumentMatch)?.valueParameter
|
||||
}
|
||||
|
||||
fun <D : CallableDescriptor> ResolvedCall<D>.usesDefaultArguments(): Boolean {
|
||||
return valueArgumentsByIndex?.any { it is DefaultValueArgument } ?: false
|
||||
}
|
||||
|
||||
|
||||
// call
|
||||
|
||||
public fun <C: ResolutionContext<C>> Call.hasUnresolvedArguments(context: ResolutionContext<C>): Boolean {
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
|
||||
public open class FileScopeProviderImpl(
|
||||
open class FileScopeProviderImpl(
|
||||
private val topLevelDescriptorProvider: TopLevelDescriptorProvider,
|
||||
private val storageManager: StorageManager,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
@@ -55,22 +55,31 @@ public open class FileScopeProviderImpl(
|
||||
override fun getImportResolver(file: KtFile) = cache(file).importResolver
|
||||
|
||||
private fun createScopeChainAndImportResolver(file: KtFile): FileData {
|
||||
val debugName = "LazyFileScope for file " + file.getName()
|
||||
val debugName = "LazyFileScope for file " + file.name
|
||||
val tempTrace = TemporaryBindingTrace.create(bindingTrace, "Transient trace for default imports lazy resolve")
|
||||
|
||||
val imports = file.importDirectives
|
||||
|
||||
val packageView = moduleDescriptor.getPackage(file.getPackageFqName())
|
||||
val packageFragment = topLevelDescriptorProvider.getPackageFragment(file.getPackageFqName())
|
||||
.sure { "Could not find fragment ${file.getPackageFqName()} for file ${file.getName()}" }
|
||||
val aliasImportNames = imports.mapNotNull { if (it.aliasName != null) it.importedFqName else null }
|
||||
|
||||
val packageView = moduleDescriptor.getPackage(file.packageFqName)
|
||||
val packageFragment = topLevelDescriptorProvider.getPackageFragment(file.packageFqName)
|
||||
.sure { "Could not find fragment ${file.packageFqName} for file ${file.name}" }
|
||||
|
||||
fun createImportResolver(indexedImports: IndexedImports, trace: BindingTrace)
|
||||
= LazyImportResolver(storageManager, qualifiedExpressionResolver, moduleDescriptor, indexedImports, trace, packageFragment)
|
||||
= LazyImportResolver(storageManager, qualifiedExpressionResolver, moduleDescriptor, indexedImports, aliasImportNames, trace, packageFragment)
|
||||
|
||||
val aliasImportResolver = createImportResolver(AliasImportsIndexed(imports), bindingTrace)
|
||||
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace)
|
||||
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace)
|
||||
val defaultAliasImportResolver = createImportResolver(AliasImportsIndexed(defaultImports), tempTrace)
|
||||
val defaultAllUnderImportResolver = createImportResolver(AllUnderImportsIndexed(defaultImports), tempTrace)
|
||||
|
||||
val defaultImportsFiltered = if (aliasImportNames.isEmpty()) { // optimization
|
||||
defaultImports
|
||||
}
|
||||
else {
|
||||
defaultImports.filter { it.isAllUnder || it.importedFqName !in aliasImportNames }
|
||||
}
|
||||
val defaultExplicitImportResolver = createImportResolver(ExplicitImportsIndexed(defaultImportsFiltered), tempTrace)
|
||||
val defaultAllUnderImportResolver = createImportResolver(AllUnderImportsIndexed(defaultImportsFiltered), tempTrace)
|
||||
|
||||
var scope: ImportingScope
|
||||
|
||||
@@ -86,14 +95,14 @@ public open class FileScopeProviderImpl(
|
||||
scope = LazyImportScope(scope, allUnderImportResolver, LazyImportScope.FilteringKind.VISIBLE_CLASSES,
|
||||
"All under imports in $debugName (visible classes)")
|
||||
|
||||
scope = LazyImportScope(scope, defaultAliasImportResolver, LazyImportScope.FilteringKind.ALL,
|
||||
"Default alias imports in $debugName")
|
||||
scope = LazyImportScope(scope, defaultExplicitImportResolver, LazyImportScope.FilteringKind.ALL,
|
||||
"Default explicit imports in $debugName")
|
||||
|
||||
scope = SubpackagesImportingScope(scope, moduleDescriptor, FqName.ROOT)
|
||||
|
||||
scope = packageView.memberScope.memberScopeAsImportingScope(scope) //TODO: problems with visibility too
|
||||
|
||||
scope = LazyImportScope(scope, aliasImportResolver, LazyImportScope.FilteringKind.ALL, "Alias imports in $debugName")
|
||||
scope = LazyImportScope(scope, explicitImportResolver, LazyImportScope.FilteringKind.ALL, "Explicit imports in $debugName")
|
||||
|
||||
val lexicalScope = LexicalScope.empty(scope, packageFragment)
|
||||
|
||||
@@ -101,7 +110,7 @@ public open class FileScopeProviderImpl(
|
||||
|
||||
val importResolver = object : ImportResolver {
|
||||
override fun forceResolveAllImports() {
|
||||
aliasImportResolver.forceResolveAllImports()
|
||||
explicitImportResolver.forceResolveAllImports()
|
||||
allUnderImportResolver.forceResolveAllImports()
|
||||
}
|
||||
|
||||
@@ -110,7 +119,7 @@ public open class FileScopeProviderImpl(
|
||||
allUnderImportResolver.forceResolveImport(importDirective)
|
||||
}
|
||||
else {
|
||||
aliasImportResolver.forceResolveImport(importDirective)
|
||||
explicitImportResolver.forceResolveImport(importDirective)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
@@ -46,7 +47,7 @@ class AllUnderImportsIndexed(allImports: Collection<KtImportDirective>) : Indexe
|
||||
override fun importsForName(name: Name) = imports
|
||||
}
|
||||
|
||||
class AliasImportsIndexed(allImports: Collection<KtImportDirective>) : IndexedImports {
|
||||
class ExplicitImportsIndexed(allImports: Collection<KtImportDirective>) : IndexedImports {
|
||||
override val imports = allImports.filter { !it.isAllUnder() }
|
||||
|
||||
private val nameToDirectives: ListMultimap<Name, KtImportDirective> by lazy {
|
||||
@@ -74,13 +75,14 @@ class LazyImportResolver(
|
||||
val qualifiedExpressionResolver: QualifiedExpressionResolver,
|
||||
val moduleDescriptor: ModuleDescriptor,
|
||||
val indexedImports: IndexedImports,
|
||||
aliasImportNames: Collection<FqName>,
|
||||
private val traceForImportResolve: BindingTrace,
|
||||
private val packageFragment: PackageFragmentDescriptor
|
||||
) : ImportResolver {
|
||||
private val importedScopesProvider = storageManager.createMemoizedFunctionWithNullableValues {
|
||||
directive: KtImportDirective ->
|
||||
val directiveImportScope = qualifiedExpressionResolver.processImportReference(
|
||||
directive, moduleDescriptor, traceForImportResolve, packageFragment) ?: return@createMemoizedFunctionWithNullableValues null
|
||||
directive, moduleDescriptor, traceForImportResolve, aliasImportNames, packageFragment) ?: return@createMemoizedFunctionWithNullableValues null
|
||||
|
||||
if (!directive.isAllUnder) {
|
||||
PlatformTypesMappedToKotlinChecker.checkPlatformTypesMappedToKotlin(
|
||||
|
||||
@@ -564,7 +564,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
ResolvedCallImpl.create(resolutionCandidate,
|
||||
TemporaryBindingTrace.create(trace, "Fake trace for fake 'this' or 'super' resolved call"),
|
||||
TracingStrategy.EMPTY,
|
||||
new DataFlowInfoForArgumentsImpl(call));
|
||||
new DataFlowInfoForArgumentsImpl(context.dataFlowInfo, call));
|
||||
resolvedCall.markCallAsCompleted();
|
||||
|
||||
trace.record(RESOLVED_CALL, call, resolvedCall);
|
||||
@@ -1448,6 +1448,32 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return components.dataFlowAnalyzer.checkType(resolveArrayAccessGetMethod(expression, context), expression, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitClass(@NotNull KtClass klass, ExpressionTypingContext context) {
|
||||
// analyze class in illegal position and write descriptor to trace but do not write to any scope
|
||||
components.localClassifierAnalyzer.processClassOrObject(
|
||||
null, context.replaceContextDependency(INDEPENDENT),
|
||||
context.scope.getOwnerDescriptor(),
|
||||
klass
|
||||
);
|
||||
return declarationInIllegalContext(klass, context);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static KotlinTypeInfo declarationInIllegalContext(
|
||||
@NotNull KtDeclaration declaration,
|
||||
@NotNull ExpressionTypingContext context
|
||||
) {
|
||||
context.trace.report(DECLARATION_IN_ILLEGAL_CONTEXT.on(declaration));
|
||||
return TypeInfoFactoryKt.noTypeInfo(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitProperty(@NotNull KtProperty property, ExpressionTypingContext context) {
|
||||
components.localVariableResolver.process(property, context, context.scope, facade);
|
||||
return declarationInIllegalContext(property, context);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KotlinTypeInfo getTypeInfoForBinaryCall(
|
||||
@NotNull Name name,
|
||||
@@ -1486,8 +1512,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitDeclaration(@NotNull KtDeclaration dcl, ExpressionTypingContext context) {
|
||||
context.trace.report(DECLARATION_IN_ILLEGAL_CONTEXT.on(dcl));
|
||||
return TypeInfoFactoryKt.noTypeInfo(context);
|
||||
return declarationInIllegalContext(dcl, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -157,15 +157,10 @@ public class ControlStructureTypingUtils {
|
||||
}
|
||||
|
||||
/*package*/ static MutableDataFlowInfoForArguments createIndependentDataFlowInfoForArgumentsForCall(
|
||||
@NotNull DataFlowInfo initialDataFlowInfo,
|
||||
final Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap
|
||||
) {
|
||||
return new MutableDataFlowInfoForArguments() {
|
||||
private DataFlowInfo initialDataFlowInfo;
|
||||
|
||||
@Override
|
||||
public void setInitialDataFlowInfo(@NotNull DataFlowInfo dataFlowInfo) {
|
||||
this.initialDataFlowInfo = dataFlowInfo;
|
||||
}
|
||||
return new MutableDataFlowInfoForArguments(initialDataFlowInfo) {
|
||||
|
||||
@Override
|
||||
public void updateInfo(@NotNull ValueArgument valueArgument, @NotNull DataFlowInfo dataFlowInfo) {
|
||||
@@ -177,25 +172,19 @@ public class ControlStructureTypingUtils {
|
||||
public DataFlowInfo getInfo(@NotNull ValueArgument valueArgument) {
|
||||
return dataFlowInfoForArgumentsMap.get(valueArgument);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataFlowInfo getResultInfo() {
|
||||
//todo merge and use
|
||||
return initialDataFlowInfo;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsForIfCall(
|
||||
@NotNull Call callForIf,
|
||||
@NotNull DataFlowInfo conditionInfo,
|
||||
@NotNull DataFlowInfo thenInfo,
|
||||
@NotNull DataFlowInfo elseInfo
|
||||
) {
|
||||
Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap = Maps.newHashMap();
|
||||
dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(0), thenInfo);
|
||||
dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(1), elseInfo);
|
||||
return createIndependentDataFlowInfoForArgumentsForCall(dataFlowInfoForArgumentsMap);
|
||||
return createIndependentDataFlowInfoForArgumentsForCall(conditionInfo, dataFlowInfoForArgumentsMap);
|
||||
}
|
||||
|
||||
/*package*/ static Call createCallForSpecialConstruction(
|
||||
|
||||
@@ -91,6 +91,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
|
||||
public KotlinTypeInfo visitIfExpression(KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
|
||||
components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, ifExpression, contextWithExpectedType.expectedType);
|
||||
|
||||
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
|
||||
KtExpression condition = ifExpression.getCondition();
|
||||
DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context);
|
||||
@@ -112,12 +114,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo)
|
||||
: result;
|
||||
}
|
||||
return TypeInfoFactoryKt.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(
|
||||
components.builtIns.getUnitType(), ifExpression,
|
||||
contextWithExpectedType, isStatement
|
||||
),
|
||||
thenInfo.or(elseInfo)
|
||||
);
|
||||
return TypeInfoFactoryKt.createTypeInfo(components.builtIns.getUnitType(), thenInfo.or(elseInfo));
|
||||
}
|
||||
if (thenBranch == null) {
|
||||
return getTypeInfoWhenOnlyOneBranchIsPresent(
|
||||
@@ -128,7 +125,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
KtBlockExpression elseBlock = psiFactory.wrapInABlockWrapper(elseBranch);
|
||||
Call callForIf = createCallForSpecialConstruction(ifExpression, ifExpression, Lists.newArrayList(thenBlock, elseBlock));
|
||||
MutableDataFlowInfoForArguments dataFlowInfoForArguments =
|
||||
createDataFlowInfoForArgumentsForIfCall(callForIf, thenInfo, elseInfo);
|
||||
createDataFlowInfoForArgumentsForIfCall(callForIf, conditionDataFlowInfo, thenInfo, elseInfo);
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall(
|
||||
callForIf, ResolveConstruct.IF, Lists.newArrayList("thenBranch", "elseBranch"),
|
||||
Lists.newArrayList(false, false),
|
||||
@@ -173,9 +170,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
|
||||
// If break or continue was possible, take condition check info as the jump info
|
||||
return TypeInfoFactoryKt
|
||||
.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement),
|
||||
resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
|
||||
return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -199,15 +194,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
} else {
|
||||
dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
|
||||
}
|
||||
return components.dataFlowAnalyzer.checkImplicitCast(
|
||||
components.dataFlowAnalyzer.checkType(
|
||||
typeInfo.replaceType(components.builtIns.getUnitType()),
|
||||
ifExpression,
|
||||
context
|
||||
),
|
||||
return components.dataFlowAnalyzer.checkType(
|
||||
typeInfo.replaceType(components.builtIns.getUnitType()),
|
||||
ifExpression,
|
||||
context,
|
||||
isStatement
|
||||
context
|
||||
).replaceDataFlowInfo(dataFlowInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ public class DataFlowAnalyzer {
|
||||
ConstantValue<?> constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, c.trace, c.expectedType);
|
||||
boolean error = new CompileTimeConstantChecker(c.trace, builtIns, true)
|
||||
.checkConstantExpressionType(constantValue, (KtConstantExpression) expression, c.expectedType);
|
||||
if (hasError != null) hasError.set(error);
|
||||
hasError.set(error);
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ public class DataFlowAnalyzer {
|
||||
if (castResult != null) return castResult.getResultType();
|
||||
|
||||
c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType));
|
||||
if (hasError != null) hasError.set(true);
|
||||
hasError.set(true);
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ public class DataFlowAnalyzer {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SmartCastResult checkPossibleCast(
|
||||
public static SmartCastResult checkPossibleCast(
|
||||
@NotNull KotlinType expressionType,
|
||||
@NotNull KtExpression expression,
|
||||
@NotNull ResolutionContext c
|
||||
@@ -271,31 +271,8 @@ public class DataFlowAnalyzer {
|
||||
return builtIns.getUnitType();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KotlinType checkImplicitCast(@Nullable KotlinType expressionType, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
|
||||
boolean isIfExpression = expression instanceof KtIfExpression;
|
||||
if (expressionType != null
|
||||
&& (context.expectedType == NO_EXPECTED_TYPE || isIfExpression)
|
||||
&& context.contextDependency == INDEPENDENT && !isStatement
|
||||
&& (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType))
|
||||
&& !DynamicTypesKt.isDynamic(expressionType)) {
|
||||
if (isIfExpression && KotlinBuiltIns.isUnit(expressionType) || isIfExpression && context.expectedType != NO_EXPECTED_TYPE) {
|
||||
return expressionType;
|
||||
}
|
||||
else {
|
||||
context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
|
||||
}
|
||||
}
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KotlinTypeInfo checkImplicitCast(@NotNull KotlinTypeInfo typeInfo, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
|
||||
return typeInfo.replaceType(checkImplicitCast(typeInfo.getType(), expression, context, isStatement));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
|
||||
public static KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
|
||||
facade.checkStatementType(
|
||||
expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT));
|
||||
context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
|
||||
@@ -303,7 +280,7 @@ public class DataFlowAnalyzer {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<KotlinType> getAllPossibleTypes(
|
||||
public static Collection<KotlinType> getAllPossibleTypes(
|
||||
@NotNull KtExpression expression,
|
||||
@NotNull DataFlowInfo dataFlowInfo,
|
||||
@NotNull KotlinType type,
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.LocalVariableResolver
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.createComponentName
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope
|
||||
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
|
||||
public class DestructuringDeclarationResolver(
|
||||
private val fakeCallResolver: FakeCallResolver,
|
||||
private val descriptorResolver: DescriptorResolver,
|
||||
private val localVariableResolver: LocalVariableResolver,
|
||||
private val typeResolver: TypeResolver,
|
||||
private val symbolUsageValidator: SymbolUsageValidator
|
||||
) {
|
||||
@@ -72,7 +72,7 @@ public class DestructuringDeclarationResolver(
|
||||
if (componentType == null) {
|
||||
componentType = ErrorUtils.createErrorType("$componentName() return type")
|
||||
}
|
||||
val variableDescriptor = descriptorResolver.resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace)
|
||||
val variableDescriptor = localVariableResolver.resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace)
|
||||
|
||||
ExpressionTypingUtils.checkVariableShadowing(writableScope, context.trace, variableDescriptor)
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ public class ExpressionTypingComponents {
|
||||
/*package*/ DataFlowAnalyzer dataFlowAnalyzer;
|
||||
/*package*/ Iterable<CallChecker> callCheckers;
|
||||
/*package*/ IdentifierChecker identifierChecker;
|
||||
/*package*/ DeclarationsCheckerBuilder declarationsCheckerBuilder;
|
||||
/*package*/ LocalVariableResolver localVariableResolver;
|
||||
|
||||
|
||||
@Inject
|
||||
public void setGlobalContext(@NotNull GlobalContext globalContext) {
|
||||
@@ -175,4 +178,14 @@ public class ExpressionTypingComponents {
|
||||
public void setCallCheckers(@NotNull Iterable<CallChecker> callCheckers) {
|
||||
this.callCheckers = callCheckers;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setDeclarationsCheckerBuilder(@NotNull DeclarationsCheckerBuilder declarationsCheckerBuilder) {
|
||||
this.declarationsCheckerBuilder = declarationsCheckerBuilder;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocalVariableResolver(@NotNull LocalVariableResolver localVariableResolver) {
|
||||
this.localVariableResolver = localVariableResolver;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.AnnotationChecker;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||
import org.jetbrains.kotlin.resolve.*;
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
|
||||
@@ -87,6 +85,7 @@ public abstract class ExpressionTypingVisitorDispatcher extends KtVisitor<Kotlin
|
||||
protected final FunctionsTypingVisitor functions;
|
||||
protected final ControlStructureTypingVisitor controlStructures;
|
||||
protected final PatternMatchingTypingVisitor patterns;
|
||||
protected final DeclarationsCheckerBuilder declarationsCheckerBuilder;
|
||||
|
||||
private ExpressionTypingVisitorDispatcher(
|
||||
@NotNull ExpressionTypingComponents components,
|
||||
@@ -98,6 +97,7 @@ public abstract class ExpressionTypingVisitorDispatcher extends KtVisitor<Kotlin
|
||||
this.controlStructures = new ControlStructureTypingVisitor(this);
|
||||
this.patterns = new PatternMatchingTypingVisitor(this);
|
||||
this.functions = new FunctionsTypingVisitor(this);
|
||||
this.declarationsCheckerBuilder = components.declarationsCheckerBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -384,6 +384,16 @@ public abstract class ExpressionTypingVisitorDispatcher extends KtVisitor<Kotlin
|
||||
return basic.visitDeclaration(dcl, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitClass(@NotNull KtClass klass, ExpressionTypingContext data) {
|
||||
return basic.visitClass(klass, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitProperty(@NotNull KtProperty property, ExpressionTypingContext data) {
|
||||
return basic.visitProperty(property, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression, ExpressionTypingContext data) {
|
||||
return basic.visitStringTemplateExpression(expression, data);
|
||||
|
||||
@@ -18,18 +18,17 @@ package org.jetbrains.kotlin.types.expressions;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.DeclarationsCheckerKt;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
@@ -105,61 +104,9 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
||||
|
||||
@Override
|
||||
public KotlinTypeInfo visitProperty(@NotNull KtProperty property, ExpressionTypingContext typingContext) {
|
||||
ExpressionTypingContext context = typingContext.replaceContextDependency(INDEPENDENT).replaceScope(scope);
|
||||
KtTypeReference receiverTypeRef = property.getReceiverTypeReference();
|
||||
if (receiverTypeRef != null) {
|
||||
context.trace.report(LOCAL_EXTENSION_PROPERTY.on(receiverTypeRef));
|
||||
}
|
||||
|
||||
KtPropertyAccessor getter = property.getGetter();
|
||||
if (getter != null) {
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_GETTER.on(getter));
|
||||
}
|
||||
|
||||
KtPropertyAccessor setter = property.getSetter();
|
||||
if (setter != null) {
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_SETTER.on(setter));
|
||||
}
|
||||
|
||||
KtExpression delegateExpression = property.getDelegateExpression();
|
||||
if (delegateExpression != null) {
|
||||
components.expressionTypingServices.getTypeInfo(delegateExpression, context);
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_DELEGATE.on(property.getDelegate()));
|
||||
}
|
||||
|
||||
VariableDescriptor propertyDescriptor = components.descriptorResolver.
|
||||
resolveLocalVariableDescriptor(scope, property, context.dataFlowInfo, context.trace);
|
||||
KtExpression initializer = property.getInitializer();
|
||||
KotlinTypeInfo typeInfo;
|
||||
if (initializer != null) {
|
||||
KotlinType outType = propertyDescriptor.getType();
|
||||
typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType));
|
||||
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||
KotlinType type = typeInfo.getType();
|
||||
// At this moment we do not take initializer value into account if type is given for a property
|
||||
// We can comment first part of this condition to take them into account, like here: var s: String? = "xyz"
|
||||
// In this case s will be not-nullable until it is changed
|
||||
if (property.getTypeReference() == null && type != null) {
|
||||
DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty(
|
||||
property, propertyDescriptor, context.trace.getBindingContext(),
|
||||
DescriptorUtils.getContainingModuleOrNull(scope.getOwnerDescriptor()));
|
||||
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context);
|
||||
// We cannot say here anything new about initializerDataFlowValue
|
||||
// except it has the same value as variableDataFlowValue
|
||||
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue));
|
||||
}
|
||||
}
|
||||
else {
|
||||
typeInfo = TypeInfoFactoryKt.noTypeInfo(context);
|
||||
}
|
||||
|
||||
ExpressionTypingUtils.checkVariableShadowing(context.scope, context.trace, propertyDescriptor);
|
||||
|
||||
scope.addVariableDescriptor(propertyDescriptor);
|
||||
DeclarationsCheckerKt.checkTypeReferences(property, context.trace);
|
||||
components.modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(property, propertyDescriptor);
|
||||
components.identifierChecker.checkDeclaration(property, context.trace);
|
||||
return typeInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(property, context));
|
||||
Pair<KotlinTypeInfo, VariableDescriptor> typeInfoAndVariableDescriptor = components.localVariableResolver.process(property, typingContext, scope, facade);
|
||||
scope.addVariableDescriptor(typeInfoAndVariableDescriptor.getSecond());
|
||||
return typeInfoAndVariableDescriptor.getFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -318,16 +265,36 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
||||
basic.resolveArrayAccessSetMethod((KtArrayAccessExpression) left, right, contextForResolve, context.trace);
|
||||
}
|
||||
rightInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(leftInfo.getDataFlowInfo()));
|
||||
components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(leftType).replaceDataFlowInfo(rightInfo.getDataFlowInfo()));
|
||||
|
||||
KotlinType expectedType = refineTypeFromPropertySetterIfPossible(context.trace.getBindingContext(), leftOperand, leftType);
|
||||
|
||||
components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(expectedType)
|
||||
.replaceDataFlowInfo(rightInfo.getDataFlowInfo()));
|
||||
basic.checkLValue(context.trace, context, leftOperand, right);
|
||||
}
|
||||
temporary.commit();
|
||||
return rightInfo.replaceType(checkAssignmentType(type, expression, contextWithExpectedType));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static KotlinType refineTypeFromPropertySetterIfPossible(
|
||||
@NotNull BindingContext bindingContext,
|
||||
@Nullable KtElement leftOperand,
|
||||
@Nullable KotlinType leftOperandType
|
||||
) {
|
||||
VariableDescriptor descriptor = BindingContextUtils.extractVariableFromResolvedCall(bindingContext, leftOperand);
|
||||
|
||||
if (descriptor instanceof PropertyDescriptor) {
|
||||
PropertySetterDescriptor setter = ((PropertyDescriptor) descriptor).getSetter();
|
||||
if (setter != null) return setter.getValueParameters().get(0).getType();
|
||||
}
|
||||
|
||||
return leftOperandType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected KotlinTypeInfo visitAssignment(KtBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
|
||||
final ExpressionTypingContext context =
|
||||
ExpressionTypingContext context =
|
||||
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceScope(scope).replaceContextDependency(INDEPENDENT);
|
||||
KtExpression leftOperand = expression.getLeft();
|
||||
if (leftOperand instanceof KtAnnotatedExpression) {
|
||||
@@ -346,15 +313,16 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
||||
return typeInfo.replaceType(checkAssignmentType(typeInfo.getType(), expression, contextWithExpectedType));
|
||||
}
|
||||
KotlinTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade);
|
||||
KotlinType leftType = leftInfo.getType();
|
||||
KotlinType expectedType = refineTypeFromPropertySetterIfPossible(context.trace.getBindingContext(), leftOperand, leftInfo.getType());
|
||||
DataFlowInfo dataFlowInfo = leftInfo.getDataFlowInfo();
|
||||
KotlinTypeInfo resultInfo;
|
||||
if (right != null) {
|
||||
resultInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo).replaceExpectedType(leftType));
|
||||
resultInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo).replaceExpectedType(expectedType));
|
||||
|
||||
dataFlowInfo = resultInfo.getDataFlowInfo();
|
||||
KotlinType rightType = resultInfo.getType();
|
||||
if (left != null && leftType != null && rightType != null) {
|
||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context);
|
||||
if (left != null && expectedType != null && rightType != null) {
|
||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, expectedType, context);
|
||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||
// We cannot say here anything new about rightValue except it has the same value as leftValue
|
||||
resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue));
|
||||
@@ -363,7 +331,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
||||
else {
|
||||
resultInfo = leftInfo;
|
||||
}
|
||||
if (leftType != null && leftOperand != null) { //if leftType == null, some other error has been generated
|
||||
if (expectedType != null && leftOperand != null) { //if expectedType == null, some other error has been generated
|
||||
basic.checkLValue(context.trace, context, leftOperand, right);
|
||||
}
|
||||
return resultInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(expression, contextWithExpectedType));
|
||||
|
||||
@@ -106,9 +106,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre
|
||||
function.checkTypeReferences(context.trace)
|
||||
components.modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(function, functionDescriptor)
|
||||
components.identifierChecker.checkDeclaration(function, context.trace)
|
||||
if (!function.hasBody() && !function.hasModifier(KtTokens.EXTERNAL_KEYWORD)) {
|
||||
context.trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor))
|
||||
}
|
||||
components.declarationsCheckerBuilder.withTrace(context.trace).checkFunction(function, functionDescriptor)
|
||||
|
||||
if (isStatement) {
|
||||
return createTypeInfo(components.dataFlowAnalyzer.checkStatementType(function, context), context)
|
||||
@@ -159,7 +157,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre
|
||||
}
|
||||
|
||||
private fun checkReservedAsync(context: ExpressionTypingContext, expression: PsiElement) {
|
||||
checkReservedPrefixWord(context.trace, expression, "async", "async block/lambda. Use 'async() { ... }' or 'async(fun...)'")
|
||||
checkReservedPrefixWord(context.trace, expression, "async", KtTokens.BINARY_OPERATIONS, "async block/lambda. Use 'async() { ... }' or 'async(fun...)'")
|
||||
}
|
||||
|
||||
private fun createFunctionLiteralDescriptor(
|
||||
|
||||
@@ -145,10 +145,11 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isExhaustive = WhenChecker.isWhenExhaustive(expression, context.trace);
|
||||
if (commonDataFlowInfo == null) {
|
||||
commonDataFlowInfo = context.dataFlowInfo;
|
||||
}
|
||||
else if (expression.getElseExpression() == null && !WhenChecker.isWhenExhaustive(expression, context.trace)) {
|
||||
else if (expression.getElseExpression() == null && !isExhaustive) {
|
||||
// Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
|
||||
// because data flow can bypass all when branches in this case
|
||||
commonDataFlowInfo = commonDataFlowInfo.or(context.dataFlowInfo);
|
||||
@@ -158,12 +159,12 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
if (resultType != null) {
|
||||
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, resultType, context);
|
||||
commonDataFlowInfo = commonDataFlowInfo.assign(resultValue, whenValue);
|
||||
if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) {
|
||||
context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression);
|
||||
}
|
||||
resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType);
|
||||
}
|
||||
return TypeInfoFactoryKt.createTypeInfo(expressionTypes.isEmpty() ? null : components.dataFlowAnalyzer.checkType(
|
||||
components.dataFlowAnalyzer.checkImplicitCast(
|
||||
resultType, expression,
|
||||
contextWithExpectedType, isStatement),
|
||||
expression, contextWithExpectedType),
|
||||
return TypeInfoFactoryKt.createTypeInfo(resultType,
|
||||
commonDataFlowInfo,
|
||||
loopBreakContinuePossible,
|
||||
contextWithExpectedType.dataFlowInfo);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.asJava
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
@@ -41,6 +42,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
@@ -172,7 +174,11 @@ public open class KtLightClassForExplicitDeclaration(
|
||||
}
|
||||
|
||||
private fun getLightClassData(): OutermostKotlinClassLightClassData {
|
||||
return getLightClassData(classOrObject)
|
||||
val lightClassData = getLightClassData(classOrObject)
|
||||
if (lightClassData !is OutermostKotlinClassLightClassData) {
|
||||
LOG.error("Invalid light class data for existing light class:\n$lightClassData\n${classOrObject.getElementTextWithContext()}")
|
||||
}
|
||||
return lightClassData as OutermostKotlinClassLightClassData
|
||||
}
|
||||
|
||||
private val _containingFile: PsiFile by lazy {
|
||||
@@ -380,7 +386,7 @@ public open class KtLightClassForExplicitDeclaration(
|
||||
override fun getStub(): KotlinClassOrObjectStub<out KtClassOrObject>? = classOrObject.stub
|
||||
|
||||
companion object {
|
||||
private val JAVA_API_STUB = Key.create<CachedValue<OutermostKotlinClassLightClassData>>("JAVA_API_STUB")
|
||||
private val JAVA_API_STUB = Key.create<CachedValue<WithFileStubAndExtraDiagnostics>>("JAVA_API_STUB")
|
||||
|
||||
private val jetTokenToPsiModifier = listOf(
|
||||
PUBLIC_KEYWORD to PsiModifier.PUBLIC,
|
||||
@@ -408,11 +414,11 @@ public open class KtLightClassForExplicitDeclaration(
|
||||
return if (internalName == null) null else JvmClassName.byInternalName(internalName).fqNameForClassNameWithoutDollars
|
||||
}
|
||||
|
||||
public fun getLightClassData(classOrObject: KtClassOrObject): OutermostKotlinClassLightClassData {
|
||||
public fun getLightClassData(classOrObject: KtClassOrObject): LightClassData {
|
||||
return getLightClassCachedValue(classOrObject).value
|
||||
}
|
||||
|
||||
public fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<OutermostKotlinClassLightClassData> {
|
||||
public fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<WithFileStubAndExtraDiagnostics> {
|
||||
val outermostClassOrObject = getOutermostClassOrObject(classOrObject)
|
||||
var value = outermostClassOrObject.getUserData(JAVA_API_STUB)
|
||||
if (value == null) {
|
||||
@@ -424,7 +430,7 @@ public open class KtLightClassForExplicitDeclaration(
|
||||
}
|
||||
|
||||
private fun getLightClassDataExactly(classOrObject: KtClassOrObject): LightClassDataForKotlinClass? {
|
||||
val data = getLightClassData(classOrObject)
|
||||
val data = getLightClassData(classOrObject) as? OutermostKotlinClassLightClassData ?: return null
|
||||
return data.dataForClass(classOrObject)
|
||||
}
|
||||
|
||||
@@ -462,5 +468,7 @@ public open class KtLightClassForExplicitDeclaration(
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(KtLightClassForExplicitDeclaration::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,13 @@ abstract class LightClassDataProvider<T : WithFileStubAndExtraDiagnostics>(
|
||||
abstract val isLocal: Boolean
|
||||
|
||||
override fun compute(): CachedValueProvider.Result<T>? {
|
||||
return CachedValueProvider.Result.create(
|
||||
computeLightClassData(),
|
||||
if (isLocal) PsiModificationTracker.MODIFICATION_COUNT else PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
|
||||
)
|
||||
}
|
||||
|
||||
private fun computeLightClassData(): T {
|
||||
val packageFqName = packageFqName
|
||||
val files = files
|
||||
|
||||
@@ -110,6 +117,7 @@ abstract class LightClassDataProvider<T : WithFileStubAndExtraDiagnostics>(
|
||||
}
|
||||
|
||||
ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub)
|
||||
return createLightClassData(javaFileStub, bindingContext, state.collectedExtraJvmDiagnostics)
|
||||
}
|
||||
catch (e: ProcessCanceledException) {
|
||||
throw e
|
||||
@@ -118,11 +126,6 @@ abstract class LightClassDataProvider<T : WithFileStubAndExtraDiagnostics>(
|
||||
logErrorWithOSInfo(e, packageFqName, null)
|
||||
throw e
|
||||
}
|
||||
|
||||
return CachedValueProvider.Result.create(
|
||||
createLightClassData(javaFileStub, bindingContext, state.collectedExtraJvmDiagnostics),
|
||||
if (isLocal) PsiModificationTracker.MODIFICATION_COUNT else PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
|
||||
)
|
||||
}
|
||||
|
||||
private fun createJavaFileStub(packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
|
||||
@@ -167,7 +170,7 @@ abstract class LightClassDataProvider<T : WithFileStubAndExtraDiagnostics>(
|
||||
}
|
||||
|
||||
class LightClassDataProviderForClassOrObject(private val classOrObject: KtClassOrObject) :
|
||||
LightClassDataProvider<OutermostKotlinClassLightClassData>(classOrObject.project) {
|
||||
LightClassDataProvider<WithFileStubAndExtraDiagnostics>(classOrObject.project) {
|
||||
|
||||
private val file: KtFile
|
||||
get() = classOrObject.getContainingKtFile()
|
||||
@@ -181,10 +184,8 @@ class LightClassDataProviderForClassOrObject(private val classOrObject: KtClassO
|
||||
override fun createLightClassData(
|
||||
javaFileStub: PsiJavaFileStub,
|
||||
bindingContext: BindingContext,
|
||||
extraDiagnostics: Diagnostics): OutermostKotlinClassLightClassData {
|
||||
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject) ?: return OutermostKotlinClassLightClassData(
|
||||
javaFileStub, extraDiagnostics, FqName.ROOT, classOrObject,
|
||||
emptyMap<KtClassOrObject, InnerKotlinClassLightClassData>())
|
||||
extraDiagnostics: Diagnostics): WithFileStubAndExtraDiagnostics {
|
||||
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassData
|
||||
|
||||
val fqName = predictClassFqName(bindingContext, classDescriptor)
|
||||
val allInnerClasses = CodegenBinding.getAllInnerClasses(bindingContext, classDescriptor)
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
|
||||
interface LightClassData
|
||||
|
||||
interface WithFileStubAndExtraDiagnostics {
|
||||
interface WithFileStubAndExtraDiagnostics: LightClassData {
|
||||
val javaFileStub: PsiJavaFileStub
|
||||
val extraDiagnostics: Diagnostics
|
||||
}
|
||||
@@ -33,6 +33,19 @@ interface LightClassDataForKotlinClass: LightClassData {
|
||||
val jvmQualifiedName: FqName
|
||||
}
|
||||
|
||||
object InvalidLightClassData: WithFileStubAndExtraDiagnostics, LightClassDataForKotlinClass {
|
||||
override val javaFileStub: PsiJavaFileStub
|
||||
get() = shouldNotBeCalled()
|
||||
override val extraDiagnostics: Diagnostics
|
||||
get() = shouldNotBeCalled()
|
||||
override val classOrObject: KtClassOrObject
|
||||
get() = shouldNotBeCalled()
|
||||
override val jvmQualifiedName: FqName
|
||||
get() = shouldNotBeCalled()
|
||||
|
||||
private fun shouldNotBeCalled(): Nothing = throw UnsupportedOperationException("Should not be called")
|
||||
}
|
||||
|
||||
data class KotlinFacadeLightClassData(
|
||||
override val javaFileStub: PsiJavaFileStub,
|
||||
override val extraDiagnostics: Diagnostics
|
||||
|
||||
@@ -160,10 +160,7 @@ public object LightClassUtil {
|
||||
}
|
||||
|
||||
if (declaration is KtPropertyAccessor) {
|
||||
val propertyParent = declaration.parent
|
||||
assert(propertyParent is KtProperty) { "JetProperty is expected to be parent of accessor" }
|
||||
|
||||
declaration = propertyParent as KtProperty
|
||||
declaration = declaration.property
|
||||
}
|
||||
|
||||
if (declaration is KtConstructor<*>) {
|
||||
|
||||
@@ -39,7 +39,7 @@ public fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Dia
|
||||
}
|
||||
|
||||
fun getDiagnosticsForClass(ktClassOrObject: KtClassOrObject): Diagnostics {
|
||||
return KtLightClassForExplicitDeclaration.getLightClassData(ktClassOrObject).extraDiagnostics
|
||||
return (KtLightClassForExplicitDeclaration.getLightClassData(ktClassOrObject) as? OutermostKotlinClassLightClassData)?.extraDiagnostics ?: Diagnostics.EMPTY
|
||||
}
|
||||
|
||||
fun doGetDiagnostics(): Diagnostics? {
|
||||
|
||||
91
compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions
vendored
Normal file
91
compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
== Direction ==
|
||||
enum class Direction {
|
||||
NORTH, SOUTH, WEST, EAST
|
||||
}
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
L1:
|
||||
<END>
|
||||
error:
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
== foo ==
|
||||
fun foo(dir: Direction): Int {
|
||||
val res: Int
|
||||
when (dir) {
|
||||
Direction.NORTH -> res = 1
|
||||
Direction.SOUTH -> res = 2
|
||||
Direction.WEST -> res = 3
|
||||
Direction.EAST -> res = 4
|
||||
}
|
||||
return res
|
||||
}
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(dir: Direction) INIT: in: {} out: {dir=D}
|
||||
magic[FAKE_INITIALIZER](dir: Direction) -> <v0> INIT: in: {dir=D} out: {dir=D}
|
||||
w(dir|<v0>) INIT: in: {dir=D} out: {dir=ID} USE: in: {dir=READ} out: {dir=READ}
|
||||
2 mark({ val res: Int when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 } return res }) INIT: in: {dir=ID} out: {dir=ID}
|
||||
v(val res: Int) INIT: in: {dir=ID} out: {dir=ID, res=D}
|
||||
mark(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} USE: in: {dir=READ, res=READ} out: {dir=READ, res=READ}
|
||||
r(dir) -> <v1> USE: in: {res=READ} out: {dir=READ, res=READ}
|
||||
mark(Direction.NORTH -> res = 1)
|
||||
mark(Direction.NORTH)
|
||||
mark(Direction.NORTH)
|
||||
r(NORTH) -> <v2>
|
||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.NORTH|<v1>, <v2>) -> <v3>
|
||||
jmp?(L4|<v3>) USE: in: {res=READ} out: {res=READ}
|
||||
L3 ['when' entry body]:
|
||||
r(1) -> <v4> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||
w(res|<v4>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID}
|
||||
L4 [next 'when' entry]:
|
||||
mark(Direction.SOUTH -> res = 2) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D}
|
||||
mark(Direction.SOUTH)
|
||||
mark(Direction.SOUTH)
|
||||
r(SOUTH) -> <v5>
|
||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.SOUTH|<v1>, <v5>) -> <v6>
|
||||
jmp?(L6|<v6>) USE: in: {res=READ} out: {res=READ}
|
||||
L5 ['when' entry body]:
|
||||
r(2) -> <v7> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||
w(res|<v7>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID}
|
||||
L6 [next 'when' entry]:
|
||||
mark(Direction.WEST -> res = 3) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D}
|
||||
mark(Direction.WEST)
|
||||
mark(Direction.WEST)
|
||||
r(WEST) -> <v8>
|
||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.WEST|<v1>, <v8>) -> <v9>
|
||||
jmp?(L8|<v9>) USE: in: {res=READ} out: {res=READ}
|
||||
L7 ['when' entry body]:
|
||||
r(3) -> <v10> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||
w(res|<v10>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID}
|
||||
L8 [next 'when' entry]:
|
||||
mark(Direction.EAST -> res = 4) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D}
|
||||
mark(Direction.EAST)
|
||||
mark(Direction.EAST)
|
||||
r(EAST) -> <v11>
|
||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.EAST|<v1>, <v11>) -> <v12>
|
||||
jmp?(L10|<v12>) USE: in: {res=READ} out: {res=READ}
|
||||
L9 ['when' entry body]:
|
||||
r(4) -> <v13> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||
w(res|<v13>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID}
|
||||
L10 [next 'when' entry]:
|
||||
magic[EXHAUSTIVE_WHEN_ELSE](when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) -> <v14> INIT: in: {dir=ID, res=D} out: {dir=ID, res=IED}
|
||||
L2 [after 'when' expression]:
|
||||
merge(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }|!<v15>, !<v16>, !<v17>, !<v18>) -> <v19> INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
||||
r(res) -> <v20> USE: in: {} out: {res=READ}
|
||||
ret(*|<v20>) L1
|
||||
L1:
|
||||
1 <END> INIT: in: {dir=ID} out: {dir=ID}
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
sink:
|
||||
<SINK> INIT: in: {dir=ID} out: {dir=ID} USE: in: {} out: {}
|
||||
=====================
|
||||
14
compiler/testData/cfg-variables/basic/ExhaustiveInitialization.kt
vendored
Normal file
14
compiler/testData/cfg-variables/basic/ExhaustiveInitialization.kt
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
enum class Direction {
|
||||
NORTH, SOUTH, WEST, EAST
|
||||
}
|
||||
|
||||
fun foo(dir: Direction): Int {
|
||||
val res: Int
|
||||
when (dir) {
|
||||
Direction.NORTH -> res = 1
|
||||
Direction.SOUTH -> res = 2
|
||||
Direction.WEST -> res = 3
|
||||
Direction.EAST -> res = 4
|
||||
}
|
||||
return res
|
||||
}
|
||||
46
compiler/testData/cfg-variables/basic/ExhaustiveInitialization.values
vendored
Normal file
46
compiler/testData/cfg-variables/basic/ExhaustiveInitialization.values
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
== Direction ==
|
||||
enum class Direction {
|
||||
NORTH, SOUTH, WEST, EAST
|
||||
}
|
||||
---------------------
|
||||
=====================
|
||||
== foo ==
|
||||
fun foo(dir: Direction): Int {
|
||||
val res: Int
|
||||
when (dir) {
|
||||
Direction.NORTH -> res = 1
|
||||
Direction.SOUTH -> res = 2
|
||||
Direction.WEST -> res = 3
|
||||
Direction.EAST -> res = 4
|
||||
}
|
||||
return res
|
||||
}
|
||||
---------------------
|
||||
<v0>: Direction NEW: magic[FAKE_INITIALIZER](dir: Direction) -> <v0>
|
||||
<v14>: * NEW: magic[EXHAUSTIVE_WHEN_ELSE](when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) -> <v14>
|
||||
dir <v1>: * NEW: r(dir) -> <v1>
|
||||
NORTH <v2>: * NEW: r(NORTH) -> <v2>
|
||||
Direction.NORTH <v2>: * COPY
|
||||
Direction.NORTH <v3>: * NEW: magic[EQUALS_IN_WHEN_CONDITION](Direction.NORTH|<v1>, <v2>) -> <v3>
|
||||
1 <v4>: Int NEW: r(1) -> <v4>
|
||||
res = 1 !<v15>: *
|
||||
SOUTH <v5>: * NEW: r(SOUTH) -> <v5>
|
||||
Direction.SOUTH <v5>: * COPY
|
||||
Direction.SOUTH <v6>: * NEW: magic[EQUALS_IN_WHEN_CONDITION](Direction.SOUTH|<v1>, <v5>) -> <v6>
|
||||
2 <v7>: Int NEW: r(2) -> <v7>
|
||||
res = 2 !<v16>: *
|
||||
WEST <v8>: * NEW: r(WEST) -> <v8>
|
||||
Direction.WEST <v8>: * COPY
|
||||
Direction.WEST <v9>: * NEW: magic[EQUALS_IN_WHEN_CONDITION](Direction.WEST|<v1>, <v8>) -> <v9>
|
||||
3 <v10>: Int NEW: r(3) -> <v10>
|
||||
res = 3 !<v17>: *
|
||||
EAST <v11>: * NEW: r(EAST) -> <v11>
|
||||
Direction.EAST <v11>: * COPY
|
||||
Direction.EAST <v12>: * NEW: magic[EQUALS_IN_WHEN_CONDITION](Direction.EAST|<v1>, <v11>) -> <v12>
|
||||
4 <v13>: Int NEW: r(4) -> <v13>
|
||||
res = 4 !<v18>: *
|
||||
when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 } <v19>: * NEW: merge(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }|!<v15>, !<v16>, !<v17>, !<v18>) -> <v19>
|
||||
res <v20>: Int NEW: r(res) -> <v20>
|
||||
return res !<v21>: *
|
||||
{ val res: Int when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 } return res } !<v21>: * COPY
|
||||
=====================
|
||||
@@ -18,29 +18,29 @@ L0:
|
||||
mark(true)
|
||||
r(true) -> <v2>
|
||||
magic[EQUALS_IN_WHEN_CONDITION](true|<v1>, <v2>) -> <v3>
|
||||
jmp?(L4|<v3>) NEXT:[mark(false -> return 0), r(1) -> <v4>]
|
||||
jmp?(L4|<v3>) NEXT:[mark(false -> return 0), r(1) -> <v4>]
|
||||
L3 ['when' entry body]:
|
||||
r(1) -> <v4>
|
||||
ret(*|<v4>) L1 NEXT:[<END>]
|
||||
- jmp(L2) NEXT:[merge(when (flag) { true -> return 1 false -> return 0 }|!<v8>, !<v9>) -> <v10>] PREV:[]
|
||||
ret(*|<v4>) L1 NEXT:[<END>]
|
||||
- jmp(L2) NEXT:[merge(when (flag) { true -> return 1 false -> return 0 }|!<v9>, !<v10>) -> <v11>] PREV:[]
|
||||
L4 [next 'when' entry]:
|
||||
mark(false -> return 0) PREV:[jmp?(L4|<v3>)]
|
||||
mark(false -> return 0) PREV:[jmp?(L4|<v3>)]
|
||||
mark(false)
|
||||
r(false) -> <v5>
|
||||
magic[EQUALS_IN_WHEN_CONDITION](false|<v1>, <v5>) -> <v6>
|
||||
jmp?(L6|<v6>) NEXT:[jmp(error), r(0) -> <v7>]
|
||||
jmp?(L6|<v6>) NEXT:[magic[EXHAUSTIVE_WHEN_ELSE](when (flag) { true -> return 1 false -> return 0 }) -> <v8>, r(0) -> <v7>]
|
||||
L5 ['when' entry body]:
|
||||
r(0) -> <v7>
|
||||
ret(*|<v7>) L1 NEXT:[<END>]
|
||||
- jmp(L2) NEXT:[merge(when (flag) { true -> return 1 false -> return 0 }|!<v8>, !<v9>) -> <v10>] PREV:[]
|
||||
ret(*|<v7>) L1 NEXT:[<END>]
|
||||
- jmp(L2) NEXT:[merge(when (flag) { true -> return 1 false -> return 0 }|!<v9>, !<v10>) -> <v11>] PREV:[]
|
||||
L6 [next 'when' entry]:
|
||||
jmp(error) NEXT:[<ERROR>] PREV:[jmp?(L6|<v6>)]
|
||||
magic[EXHAUSTIVE_WHEN_ELSE](when (flag) { true -> return 1 false -> return 0 }) -> <v8> PREV:[jmp?(L6|<v6>)]
|
||||
L2 [after 'when' expression]:
|
||||
- merge(when (flag) { true -> return 1 false -> return 0 }|!<v8>, !<v9>) -> <v10> PREV:[]
|
||||
- merge(when (flag) { true -> return 1 false -> return 0 }|!<v9>, !<v10>) -> <v11>
|
||||
L1:
|
||||
1 <END> NEXT:[<SINK>] PREV:[ret(*|<v4>) L1, ret(*|<v7>) L1]
|
||||
1 <END> NEXT:[<SINK>] PREV:[ret(*|<v4>) L1, ret(*|<v7>) L1]
|
||||
error:
|
||||
<ERROR> PREV:[jmp(error)]
|
||||
<ERROR> PREV:[]
|
||||
sink:
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
=====================
|
||||
|
||||
@@ -7,15 +7,16 @@ fun foo(flag: Boolean): Int {
|
||||
}
|
||||
---------------------
|
||||
<v0>: Boolean NEW: magic[FAKE_INITIALIZER](flag: Boolean) -> <v0>
|
||||
<v8>: * NEW: magic[EXHAUSTIVE_WHEN_ELSE](when (flag) { true -> return 1 false -> return 0 }) -> <v8>
|
||||
flag <v1>: * NEW: r(flag) -> <v1>
|
||||
true <v2>: * NEW: r(true) -> <v2>
|
||||
true <v3>: * NEW: magic[EQUALS_IN_WHEN_CONDITION](true|<v1>, <v2>) -> <v3>
|
||||
1 <v4>: Int NEW: r(1) -> <v4>
|
||||
return 1 !<v8>: *
|
||||
return 1 !<v9>: *
|
||||
false <v5>: * NEW: r(false) -> <v5>
|
||||
false <v6>: * NEW: magic[EQUALS_IN_WHEN_CONDITION](false|<v1>, <v5>) -> <v6>
|
||||
0 <v7>: Int NEW: r(0) -> <v7>
|
||||
return 0 !<v9>: *
|
||||
when (flag) { true -> return 1 false -> return 0 } <v10>: * NEW: merge(when (flag) { true -> return 1 false -> return 0 }|!<v8>, !<v9>) -> <v10>
|
||||
{ when (flag) { true -> return 1 false -> return 0 } } <v10>: * COPY
|
||||
return 0 !<v10>: *
|
||||
when (flag) { true -> return 1 false -> return 0 } <v11>: * NEW: merge(when (flag) { true -> return 1 false -> return 0 }|!<v9>, !<v10>) -> <v11>
|
||||
{ when (flag) { true -> return 1 false -> return 0 } } <v11>: * COPY
|
||||
=====================
|
||||
|
||||
14
compiler/testData/codegen/box/controlStructures/returnsNothing/ifElse.kt
vendored
Normal file
14
compiler/testData/codegen/box/controlStructures/returnsNothing/ifElse.kt
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
var flag = true
|
||||
|
||||
fun exit(): Nothing = null!!
|
||||
|
||||
fun box(): String {
|
||||
val a: String
|
||||
if (flag) {
|
||||
a = "OK"
|
||||
}
|
||||
else {
|
||||
exit()
|
||||
}
|
||||
return a
|
||||
}
|
||||
12
compiler/testData/codegen/box/controlStructures/returnsNothing/inlineMethod.kt
vendored
Normal file
12
compiler/testData/codegen/box/controlStructures/returnsNothing/inlineMethod.kt
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
inline fun exit(): Nothing = null!!
|
||||
|
||||
fun box(): String {
|
||||
val a: String
|
||||
try {
|
||||
a = "OK"
|
||||
}
|
||||
catch (e: Exception) {
|
||||
exit()
|
||||
}
|
||||
return a
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user