Compare commits

...

112 Commits

Author SHA1 Message Date
Ilya Gorbunov
994eceabc0 Deprecate dropWhileTo and takeWhileTo on CharSequences.
(cherry picked from commit 14f17e7)
2016-01-19 07:14:30 +03:00
Ilya Gorbunov
e46544cc5e Deprecate 'comparator { ... }' in favor of Comparator SAM-constructor. Provide SAM-like constructor for JS.
(cherry picked from commit 5bbce7a)
2016-01-19 07:14:18 +03:00
Ilya Gorbunov
c0668334b4 Move comparison related functions to kotlin.comparisons, update imports in stdlib.
(cherry picked from commit 67ef790)
2016-01-19 07:14:06 +03:00
Ilya Gorbunov
1ad2c6e4a7 Deprecate getOrImplicitDefault to make it private later.
(cherry picked from commit c250921)
2016-01-19 07:13:50 +03:00
Ilya Gorbunov
26b3c06963 Provide mutableSetOf and mutableMapOf
#KT-9663 Fixed
(cherry picked from commit aafd790)
2016-01-19 07:13:38 +03:00
Ilya Gorbunov
362f1641b8 Provide mutableListOf.
Deprecate linkedListOf.
#KT-9663
(cherry picked from commit fe8ba4d)
2016-01-19 07:13:27 +03:00
Stanislav Erokhin
889126cc25 Possible fix for KT-10603. 2016-01-18 19:47:12 +03:00
Mikhail Glukhikh
000b14d86e Diagnostic message fixed for 'PRIVATE_SETTER_FOR_OPEN_PROPERTY'
(cherry picked from commit 380e2dd)
2016-01-18 14:07:18 +03:00
Valentin Kipyatkov
258ae08a6e "Rename on import" hides importing of the same symbol by other imports with the original name
(cherry picked from commit 5a079de)
2016-01-18 13:11:05 +03:00
Valentin Kipyatkov
a0d9271e00 Do not use incorrect import
(cherry picked from commit 4704188)
2016-01-18 13:07:15 +03:00
Valentin Kipyatkov
3ceba38a78 Minor
(cherry picked from commit 866f3f1)
2016-01-18 13:07:05 +03:00
Valentin Kipyatkov
c28e9b0f3c Renamed class
(cherry picked from commit 8984368)
2016-01-18 13:06:53 +03:00
Mikhail Glukhikh
5b23a86f6e Cleanup: DataFlowAnalyzer, ignore MethodMayBeStatic for SmartCastManager
(cherry picked from commit c48c3fc)
2016-01-18 10:05:39 +03:00
Mikhail Glukhikh
8c1cd3a1ed Separate UNSAFE_IMPLICIT_INVOKE_CALL diagnostics introduced (see KT-8252)
(cherry picked from commit 6157ebe)
2016-01-18 10:05:30 +03:00
Mikhail Glukhikh
ab7c2cb82a Minor
(cherry picked from commit fe11b5a)
2016-01-18 10:05:14 +03:00
Mikhail Glukhikh
b707da415c Nullable function-like property call is prohibited now #KT-8252 Fixed
(cherry picked from commit 1049d4c)
2016-01-18 10:05:01 +03:00
Mikhail Glukhikh
ddfdff0b30 initialDataFlowInfoForArguments introduced in CallExpressionResolver methods #KT-10175 Fixed
(cherry picked from commit 0f80df7)
2016-01-18 10:03:47 +03:00
Mikhail Glukhikh
e7c81388b7 Get rid of MutableDataFlorInfoForArguments.setInitialDataFlowInfo
(cherry picked from commit 16d97ab)
2016-01-18 10:03:35 +03:00
Mikhail Glukhikh
b00d05ecad Minor refactoring of getQualifiedExpressionTypeInfo
(cherry picked from commit 5ceb973)
2016-01-18 10:03:15 +03:00
Pavel V. Talanov
f5ead3507a Avoid throwing on trying to obtain module info by some unexpected light element 2016-01-17 14:02:44 +03:00
Pavel V. Talanov
a4b9c32933 Analyze local variable declarations in expression position
Fixes exception on invalid code "val c = 1 < val Int.f: Int = 3"
2016-01-17 14:02:44 +03:00
Pavel V. Talanov
8839e9f707 Minor: prettify code after autoconversion 2016-01-17 14:02:43 +03:00
Pavel V. Talanov
4f9b441a66 Refactor: extract type checking code for local variables to a separate component, extract code that creates descriptors for local variable out of DescriptorResolver 2016-01-17 14:02:43 +03:00
Pavel V. Talanov
e4116624c3 AnnotationUtil: Do not throw on ErrorValue constant 2016-01-17 14:02:42 +03:00
Pavel V. Talanov
4dd91c0a81 Minor: fix missed error marker in test data 2016-01-17 14:02:42 +03:00
Pavel V. Talanov
47ad2370e3 Do not build light classes for local classes when the there is no class descriptor
These cases should be dealt with and this is only a way to degrade gracefully
2016-01-17 14:02:41 +03:00
Pavel V. Talanov
39598530d5 Parse local interfaces in expression position 2016-01-17 14:02:41 +03:00
Pavel V. Talanov
fa98553461 Create class descriptors for local classes in illegal positions
Typecheck code in illegal selector position
Fixes exceptions on "val p = A.class" erroneous code
2016-01-17 14:02:40 +03:00
Pavel V. Talanov
94369a24d5 Fix codegen predictor for properties and property accessors 2016-01-17 14:02:40 +03:00
Pavel V. Talanov
036828b923 Add and use KtPropertyAccessor#getProperty() 2016-01-17 14:02:39 +03:00
Pavel V. Talanov
5f27032bae IDELightClassGenerationSupport: fix a problem when marking source root as both source and library source led to SO
# KT-10413 Fixed
2016-01-17 14:02:39 +03:00
Pavel V. Talanov
cee7724d9f Minor, ProjectRootsUtil: extract a couple of utils and use them 2016-01-17 14:02:38 +03:00
Pavel V. Talanov
a86fcf1141 Add better diagnostic for "could not create stub for nested class" failures 2016-01-17 14:02:38 +03:00
Pavel V. Talanov
02262534db Fix an exception caused by objects literals in supertype list not considered local 2016-01-17 14:02:36 +03:00
Ilya Gorbunov
2706dd1d8e Annotate Iterable.forEach and Map.forEach with HidesMembers.
#KT-10538 Fixed
#KT-10479 Fixed
(cherry picked from commit 3d5e415)
2016-01-16 18:48:39 +03:00
Ilya Gorbunov
2b1b54bc28 Hide listFiles as it's no more preferred by overload resolution over synthetic SAM invocations.
#KT-10652 Fixed
(cherry picked from commit 56ddd49)
2016-01-16 17:51:23 +03:00
Ilya Gorbunov
0cd0f54b21 Rename defaultBufferSize to DEFAULT_BUFFER_SIZE.
(cherry picked from commit a64c1ff)
2016-01-16 17:51:00 +03:00
Ilya Gorbunov
24522175e5 J2K: Avoid to convert to deprecated String methods, namely toByteArray.
(cherry picked from commit ddcafdd)
2016-01-16 17:50:52 +03:00
Ilya Gorbunov
74d7d109b9 Deprecate functions taking charset name as string.
(cherry picked from commit ac15807)
2016-01-16 17:50:47 +03:00
Stanislav Erokhin
fe5df28123 Create new HidesMembers annotation 2016-01-15 23:39:44 +03:00
Dmitry Jemerov
62f92f2453 streamlined plugin update logic
(cherry picked from commit 63dd0fc)
2016-01-15 19:20:27 +01:00
Dmitry Jemerov
839c1d29d3 plugin autoupdate fixed: always provide plugin descriptor with new version number to plugin downloader
(cherry picked from commit 765d58b)
2016-01-15 19:20:16 +01:00
NataliaUkhorskaya
ecddf5ee20 Gradle plugin: fix compatibility with android-gradle plugin 2.0.0-alpha5
#KT-10676 Fixed
(cherry picked from commit d4fcb59)
2016-01-15 20:59:10 +03:00
Michael Bogdanov
b7b099e795 Prohibit super calls with default parameters
(cherry picked from commit 9b3d974)
2016-01-15 17:55:54 +03:00
Dmitry Petrov
292055276d KT-10646, KT-10647:
Move IMPLICIT_CAST_TO_UNIT_OR_ANY to ControlFlowInformationProvider
(where checks for 'if' and 'when' used as expressions are performed).
(cherry picked from commit f54de08)
2016-01-15 16:07:54 +03:00
Dmitry Petrov
90491cf683 KT-6646, KT-10482:
when a method (or a property getter) returns Nothing, emit
  ACONST_NULL
  ATHROW
after a call so that class files verifier knows that this is an exit point in a method.
Note that if an inline method returning Nothing throws an exception explicitly
(or via a chain of inline methods), this code will be deleted by DCE.
(cherry picked from commit b736880)
2016-01-15 16:07:53 +03:00
Dmitry Petrov
d4c55159bd Drop "substitute with upper bounds" mode for calls with mapped arguments
(we don't need it since we build constraint system for generic types).
Cleanup tests.
(cherry picked from commit 74c1390)
2016-01-15 16:07:53 +03:00
Dmitry Petrov
b7451c6e2b Check specificity relation for generant types of the constraint system.
Otherwise we can't properly chose between 'dynamic' and generic type.
(cherry picked from commit 4b3290d)
2016-01-15 16:07:53 +03:00
Dmitry Petrov
3941ac308d stdlib fixes:
- 'sequence(initialValue: T?, ...)' should have LowPriorityInOverloadResolution
(otherwise 'sequence({...}, {...})' is ambiguous).
- 'copyOf' and 'copyOfRange' should be defined for 'Array<T>' only
('Array<out T>' version always loses to 'Array<T>', since the second one
is always more specific).
(cherry picked from commit c97294a)
2016-01-15 16:07:53 +03:00
Dmitry Petrov
28f7ed010b Use constraint system for comparing for specificity against a generic signature.
Drop "discrimiate generics" mode where it's unneeded.
(cherry picked from commit 02daeac)
2016-01-15 16:07:52 +03:00
Yan Zhulanow
9a19322bac More precise diagnostic messages about "operator modifier is not applicable" 2016-01-15 20:14:27 +09:00
Yan Zhulanow
cca091991f "Inapplicable operator modifier" and "Inapplicable infix modifier" are now errors 2016-01-15 20:14:27 +09:00
Stanislav Erokhin
3933b8a48f Change resolution priority about implicit receivers and synthesized member-like descriptors.
Change resolution to consider extensions to implicit receiver before members of another implicit receiver.
Make synthesized member-like extensions resolve right after the members.

#KT-10510 Fixed
#KT-10219 Fixed
2016-01-14 21:37:39 +03:00
Stanislav Erokhin
a69bd11a77 Minor. Extract tower data creation to separate function. 2016-01-14 21:36:26 +03:00
Stanislav Erokhin
e3d45d22ac Move levels to TowerResolver 2016-01-14 21:36:25 +03:00
Zalim Bashorov
d6b04166c0 Don't fail when create IncrementalCacheImpl for target without output directory, and fail when try to use this info instead.
#KT-10505 Fixed
(cherry picked from commit c1dbfee)
2016-01-14 21:26:24 +03:00
Zalim Bashorov
dbcb91a118 Don't fail when output directory not specified for "friend" build target
#KT-10505 Fixed
(cherry picked from commit d9af947)
2016-01-14 21:25:52 +03:00
Zalim Bashorov
58b1cf42ae Report error when output directory not specified for build target
#KT-10505 Fixed
(cherry picked from commit 3df091e)
2016-01-14 21:25:39 +03:00
Valentin Kipyatkov
6221c76578 KT-10664 No code completion after variable initialized with elvis
#KT-10664 Fixed
(cherry picked from commit 7c6a33a)
2016-01-14 19:24:25 +03:00
Nikolay Krasko
7ffda5d985 Bootstrap against beta5 and temporary disable auto-increment 2016-01-14 17:24:16 +03:00
Mikhail Glukhikh
c40227d93f Implicit exhaustive whens now have exception in else branch #KT-8700 Fixed
(cherry picked from commit 7d6ccc4)
2016-01-14 16:30:31 +03:00
Mikhail Glukhikh
6b411b900e Implicit exhaustive when check for definite variable initialization (KT-8700)
(cherry picked from commit 011a9f2)
2016-01-14 16:30:28 +03:00
Mikhail Glukhikh
95cad01d97 ControlFlowInfo introduced to store variable states, related refactoring
(cherry picked from commit 52c3fb0)
2016-01-14 16:30:24 +03:00
Mikhail Glukhikh
0893dafab3 InitState / VariableUseState / VariableControlFlowState moved to a separate file
(cherry picked from commit 0f3997c)
2016-01-14 16:30:20 +03:00
Mikhail Glukhikh
118b619da3 PseudocodeVariablesData converted to Kotlin
(cherry picked from commit 4c4456c)
2016-01-14 16:30:16 +03:00
Mikhail Glukhikh
195bbfe1ba PseudocodeVariablesData.java --> PseudocodeVariablesData.kt
(cherry picked from commit 7179b37)
2016-01-14 16:30:12 +03:00
Mikhail Glukhikh
8dd3f438d8 Exhaustive whens without else and 'Nothing' as the result are considered 'implicit exhaustive'
(cherry picked from commit b938949)
2016-01-14 16:30:08 +03:00
Mikhail Glukhikh
741cdbe0a3 Introduced binding context storage for implicit exhaustive when (KT-8700)
(cherry picked from commit d62d7dd)
2016-01-14 16:30:04 +03:00
Mikhail Glukhikh
94b39326de CFG exhaustive when else instruction for KT-8700
(cherry picked from commit b805ce0)
2016-01-14 16:30:00 +03:00
Mikhail Glukhikh
c3432df3c8 Implemented missed checks for local functions #KT-10449 Fixed
Relevant code/test fixes
(cherry picked from commit 99a32b9)
2016-01-14 16:29:56 +03:00
Mikhail Glukhikh
c5a84077f0 Implicit callable type check refactoring, additional check for property with explicit Nothing type
(cherry picked from commit 15746cb)
2016-01-14 16:29:52 +03:00
Mikhail Glukhikh
7f36a3ea3a Intersection types are no more allowed in signatures #KT-10244 Fixed
(cherry picked from commit 6b8b39a)
2016-01-14 16:29:48 +03:00
Denis Zharkov
edf7200e5d Adjust type approximation to broken code and missing dependencies
- Do not run approximation if arguments number is different
- Add nullable Any? as supertype to MissingDependencyErrorClass

The latter is needed because otherwise TypeArgument.isConsistent became false
2016-01-14 15:38:07 +03:00
Denis Zharkov
8bf0f32b18 Fix supertypes calculation for types with projections
Use captured types as replacement for non top-level entries

 #KT-7296 Fixed
2016-01-14 15:38:06 +03:00
Denis Zharkov
9d08d51377 Fix captured approximation for case of flexible types
#KT-9294 Fixed
2016-01-14 15:38:05 +03:00
Denis Zharkov
fb09160d87 Make project compilable after member scope refinement 2016-01-14 15:38:05 +03:00
Denis Zharkov
30796c5dd2 Replace type entries of JsContext<*> with JsContext<JsNode>
It's needed to make method calls (e.g. replaceMe) on it typesafe,
otherwise it's value parameter type is subtype of captured
2016-01-14 15:38:04 +03:00
Denis Zharkov
69ba93fa16 Refine type from property setter parameter on assignment
In most cases these types are equals, the only known exception is
var-property contained in projected type member scope (see test data)
2016-01-14 15:38:04 +03:00
Denis Zharkov
bc06c0b264 Refine member scope for types with projections
Instead of erasing descriptors with conflicting substitution,
use invariant CapturedType(<projection>) as replacement for type parameter
within default member scope.

After substitution leave such types 'as is' everywhere except return types,
use common approximation for them.

 #KT-9294 In Progress
 #KT-5411 Fixed
 #KT-8647 Fixed

 #KT-9462 Fixed
 #KT-9893 Fixed
 #KT-7581 Fixed
 #KT-7296 In Progress
2016-01-14 15:38:03 +03:00
Denis Zharkov
009794dfd2 Extract and normalize member scope calculation
Basically it's wrong to use original types' member scope
as a worker for SubstitutionScope.
Member scope should always be determined by type constructor's default one
and substitution/arguments

 #KT-10448 Fixed
2016-01-14 15:38:00 +03:00
Denis Zharkov
cbaf84754c Minor. Add tests checking not-null assertions
More precisely these tests check cases when expected type
was somehow obtained from captured type (in member scope with projections)
2016-01-14 15:35:51 +03:00
Denis Zharkov
14d3208b7a Introduce CustomSubstitutionCapability.substitutionToComposeWith
Mainly it's needed to prevent creation of subsituions composition
everytime we replacing arguments, because it's both unoptimal and wrong

When replace arguments in `A<E, F>` with <String, E> you got `A<String, String>`
as a result, that is unexpected.

But composition is only needed when previous substituion was abnormal
(e.g. RawSubsitution that should actually wrap new arguments), see RawTypes tests
2016-01-14 15:35:51 +03:00
Denis Zharkov
9cfc11d0df Preserve type capabilities of captured type 2016-01-14 15:35:50 +03:00
Denis Zharkov
499ddeec82 Minor. Cleanup code 2016-01-14 15:35:50 +03:00
Denis Zharkov
63508c8e66 Minor. Move declaration closer to usages and optimize imports 2016-01-14 15:35:49 +03:00
Denis Zharkov
0e5b1ed884 Replace unchecked 'is' on local class with unchecked 'as'
It became unchecked after latest changes (see parent commit)
2016-01-14 15:35:48 +03:00
Denis Zharkov
8469003416 Fix type parameter list for local classes
Add captured parameters from enclosing functions

 #KT-9584 Fixed
2016-01-14 15:35:48 +03:00
Denis Zharkov
fb0e6c758d Clean project code after #KT-3996 fix
No overload resolution ambiguity in smart-casts
2016-01-14 15:35:47 +03:00
Denis Zharkov
7166b69c5f Fix wrong contract assumption
Currently SamAdapterOverridabilityCondition can be called
even for incompatible descriptors

 #KT-10486 Fixed
2016-01-14 15:35:47 +03:00
Nikolay Krasko
53f92f35d4 Remove assert that isn't valid in UpSource (UP-5742) 2016-01-12 21:41:54 +03:00
Michael Bogdanov
b4840bddc5 Always generate ACC_SUPER flag for all classes; Fix for KT-10260: java.lang.VerifyError in Android 4.x when Instant Run is used
#KT-10260 Fixed
(cherry picked from commit 0274ce4)
2016-01-11 13:02:34 +03:00
Pavel V. Talanov
3b253d10f3 Fix a problem caused by getting project using an invalid psi element 2015-12-23 16:47:40 +03:00
Yan Zhulanow
19c3382a19 Always compile Android projects without JDK in classpath #KT-10479 2015-12-23 16:43:47 +03:00
Dmitry Petrov
65a1c7b040 Fix KT-10472: compare all overloads including varargs in a single pass. 2015-12-22 17:10:55 +03:00
Stanislav Erokhin
0cffd955ea Hack for unavailable archive.apache.org: use bintray.com instead
(cherry picked from commit de6f520)
2015-12-22 16:47:43 +03:00
Nikolay Krasko
3847dd9794 Make add test lib quick-fix applicable only when there is unresolved import 2015-12-22 16:24:08 +03:00
Nikolay Krasko
bc5911e64b Add library for Gradle 2015-12-22 16:24:07 +03:00
Nikolay Krasko
6f96192e2f Determine maven library version from kotlin-stdlib 2015-12-22 16:24:07 +03:00
Nikolay Krasko
9f378345fc Add libraries to maven 2015-12-22 16:24:07 +03:00
Nikolay Krasko
760052b917 Add kotlin-test.jar to classpath quickfix 2015-12-22 16:24:07 +03:00
Sergey Mashkov
035fe187c1 kotlin-test: exclude OnlyInpuType from dist kotlin-test as well 2015-12-21 15:50:46 +03:00
Sergey Mashkov
e1a0caa27b kotlin-test: exclude OnlyInputTypes annotation from jar 2015-12-21 15:50:38 +03:00
Sergey Mashkov
e0061f5f37 rename kotlin.test to kotlin-test 2015-12-21 15:50:27 +03:00
Nikolay Krasko
128ddd5237 Enable version auto-increment and bootstrapping 2015-12-21 13:52:18 +03:00
Dmitry Petrov
b8094b2073 Prohibit functions (and constructors) with multiple vararg parameters.
(cherry picked from commit fab072e)
2015-12-21 13:28:27 +03:00
Andrey Breslav
e3924564ed Reserve "async* {}", extend the quick-fix 2015-12-21 07:07:15 +03:00
Andrey Breslav
3bb8d69760 Minor. Additional test for "async {}" 2015-12-21 07:07:15 +03:00
Stanislav Erokhin
c926c135ca Completion fix for reserved 'async' syntax 2015-12-21 07:07:15 +03:00
Nikolay Krasko
8714672ced Quick fix for deprecated async syntax 2015-12-21 07:07:15 +03:00
Ilya Gorbunov
bbc8941d76 Deprecate IndexingIterable and IndexingIterator and provide Iterator.withIndex() instead of the latter. 2015-12-19 09:58:28 +03:00
Ilya Gorbunov
2a18ab2d76 Deprecate some top-level constants to make 'em private later. 2015-12-19 09:58:26 +03:00
Ilya Chernikov
280c41a64d Working around cancellation-related exception in case of different versions of daemon and client 2015-12-18 22:20:59 +01:00
564 changed files with 7093 additions and 2359 deletions

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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
)

View File

@@ -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>()

View File

@@ -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",

View File

@@ -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",

View File

@@ -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(),

View File

@@ -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",

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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(),

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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()
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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()))
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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
}
}
}

View File

@@ -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 {
}
}
}
}
}
);

View File

@@ -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);
}
}
}

View File

@@ -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
}

View File

@@ -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>>

View File

@@ -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;
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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");
}
}

View File

@@ -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());
}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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");

View File

@@ -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

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -143,4 +143,9 @@ public class KtPropertyAccessor extends KtDeclarationStub<KotlinPropertyAccessor
public boolean hasInitializer() {
return getInitializer() != null;
}
@NotNull
public KtProperty getProperty() {
return (KtProperty) getParent();
}
}

View File

@@ -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;
}

View File

@@ -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))
}
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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>) {

View File

@@ -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(

View File

@@ -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
}
}

View File

@@ -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))
}
}

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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>> {

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
};
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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)
}
}
}

View File

@@ -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(

View File

@@ -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

View File

@@ -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(

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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(

View File

@@ -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);

View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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<*>) {

View File

@@ -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? {

View 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: {}
=====================

View 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
}

View 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
=====================

View File

@@ -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>]
=====================

View File

@@ -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
=====================

View 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
}

View 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