Analysis API: add test covering get missing when branches

Also fix FIR implementation to generate boolean branches in the same
order as FE1.0.
This commit is contained in:
Tianyu Geng
2021-11-02 17:37:33 -07:00
committed by Ilya Kirillov
parent 8ba2e4d221
commit 2c2851af6e
37 changed files with 455 additions and 17 deletions
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.descriptors.test.components
import org.jetbrains.kotlin.analysis.api.descriptors.test.KtFe10FrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.base.test.components.AbstractWhenMissingCasesTest
abstract class AbstractKtFe10WhenMissingCasesTest : AbstractWhenMissingCasesTest(KtFe10FrontendApiTestConfiguratorService)
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.descriptors.test.components;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/components/whenMissingCases")
@TestDataPath("$PROJECT_ROOT")
public class KtFe10WhenMissingCasesTestGenerated extends AbstractKtFe10WhenMissingCasesTest {
@Test
public void testAllFilesPresentInWhenMissingCases() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/whenMissingCases"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("boolean_else.kt")
public void testBoolean_else() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/boolean_else.kt");
}
@Test
@TestMetadata("boolean_empty.kt")
public void testBoolean_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/boolean_empty.kt");
}
@Test
@TestMetadata("boolean_partial.kt")
public void testBoolean_partial() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/boolean_partial.kt");
}
@Test
@TestMetadata("enum_else.kt")
public void testEnum_else() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/enum_else.kt");
}
@Test
@TestMetadata("enum_empty.kt")
public void testEnum_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/enum_empty.kt");
}
@Test
@TestMetadata("enum_partial.kt")
public void testEnum_partial() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/enum_partial.kt");
}
@Test
@TestMetadata("nothing.kt")
public void testNothing() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nothing.kt");
}
@Test
@TestMetadata("nullableBoolean.kt")
public void testNullableBoolean() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableBoolean.kt");
}
@Test
@TestMetadata("nullableEnum.kt")
public void testNullableEnum() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableEnum.kt");
}
@Test
@TestMetadata("nullableNothing.kt")
public void testNullableNothing() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableNothing.kt");
}
@Test
@TestMetadata("nullableSealedClass_empty.kt")
public void testNullableSealedClass_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableSealedClass_empty.kt");
}
@Test
@TestMetadata("sealedClass_else.kt")
public void testSealedClass_else() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/sealedClass_else.kt");
}
@Test
@TestMetadata("sealedClass_empty.kt")
public void testSealedClass_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/sealedClass_empty.kt");
}
@Test
@TestMetadata("sealedClass_partial.kt")
public void testSealedClass_partial() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/sealedClass_partial.kt");
}
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.base.test.components.AbstractWhenMissingCasesTest
abstract class AbstractFirWhenMissingCasesTest : AbstractWhenMissingCasesTest(FirFrontendApiTestConfiguratorService)
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.fir.components;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/components/whenMissingCases")
@TestDataPath("$PROJECT_ROOT")
public class FirWhenMissingCasesTestGenerated extends AbstractFirWhenMissingCasesTest {
@Test
public void testAllFilesPresentInWhenMissingCases() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/whenMissingCases"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("boolean_else.kt")
public void testBoolean_else() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/boolean_else.kt");
}
@Test
@TestMetadata("boolean_empty.kt")
public void testBoolean_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/boolean_empty.kt");
}
@Test
@TestMetadata("boolean_partial.kt")
public void testBoolean_partial() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/boolean_partial.kt");
}
@Test
@TestMetadata("enum_else.kt")
public void testEnum_else() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/enum_else.kt");
}
@Test
@TestMetadata("enum_empty.kt")
public void testEnum_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/enum_empty.kt");
}
@Test
@TestMetadata("enum_partial.kt")
public void testEnum_partial() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/enum_partial.kt");
}
@Test
@TestMetadata("nothing.kt")
public void testNothing() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nothing.kt");
}
@Test
@TestMetadata("nullableBoolean.kt")
public void testNullableBoolean() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableBoolean.kt");
}
@Test
@TestMetadata("nullableEnum.kt")
public void testNullableEnum() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableEnum.kt");
}
@Test
@TestMetadata("nullableNothing.kt")
public void testNullableNothing() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableNothing.kt");
}
@Test
@TestMetadata("nullableSealedClass_empty.kt")
public void testNullableSealedClass_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/nullableSealedClass_empty.kt");
}
@Test
@TestMetadata("sealedClass_else.kt")
public void testSealedClass_else() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/sealedClass_else.kt");
}
@Test
@TestMetadata("sealedClass_empty.kt")
public void testSealedClass_empty() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/sealedClass_empty.kt");
}
@Test
@TestMetadata("sealedClass_partial.kt")
public void testSealedClass_partial() throws Exception {
runTest("analysis/analysis-api/testData/components/whenMissingCases/sealedClass_partial.kt");
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.impl.base.test.components
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractWhenMissingCasesTest(
configurator: FrontendApiTestConfiguratorService
) : AbstractHLApiSingleFileTest(configurator) {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
super.doTestByFileStructure(ktFile, module, testServices)
val whenExpression = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtWhenExpression>(ktFile)
val actual = executeOnPooledThreadInReadAction {
analyseForTest(whenExpression) {
buildString {
for (missingCase in whenExpression.getMissingCases()) {
appendLine(missingCase::class.simpleName + " - " + missingCase.branchConditionText)
}
}
}
}
testServices.assertions.assertEqualsToFile(testDataFileSibling(".txt"), actual)
}
}
@@ -0,0 +1,5 @@
fun test(b: Boolean) {
<caret>when(b) {
else -> {}
}
}
@@ -0,0 +1,2 @@
True - true
False - false
@@ -0,0 +1,4 @@
fun test(b: Boolean) {
<caret>when(b) {
}
}
@@ -0,0 +1,2 @@
True - true
False - false
@@ -0,0 +1,5 @@
fun test(b: Boolean) {
<caret>when(b) {
true -> {}
}
}
@@ -0,0 +1 @@
False - false
@@ -0,0 +1,9 @@
enum class MyEnum {
A, B, C
}
fun test(e: MyEnum) {
<caret>when (e) {
else -> {}
}
}
@@ -0,0 +1,3 @@
EnumCheckIsMissing - MyEnum.A
EnumCheckIsMissing - MyEnum.B
EnumCheckIsMissing - MyEnum.C
@@ -0,0 +1,8 @@
enum class MyEnum {
A, B, C
}
fun test(e: MyEnum) {
<caret>when (e) {
}
}
@@ -0,0 +1,3 @@
EnumCheckIsMissing - MyEnum.A
EnumCheckIsMissing - MyEnum.B
EnumCheckIsMissing - MyEnum.C
@@ -0,0 +1,9 @@
enum class MyEnum {
A, B, C
}
fun test(e: MyEnum) {
<caret>when (e) {
MyEnum.A -> {}
}
}
@@ -0,0 +1,2 @@
EnumCheckIsMissing - MyEnum.B
EnumCheckIsMissing - MyEnum.C
@@ -0,0 +1,4 @@
fun test(n: Nothing) {
<caret>when (n) {
}
}
@@ -0,0 +1 @@
Unknown - else
@@ -0,0 +1,4 @@
fun test(b: Boolean?) {
<caret>when(b) {
}
}
@@ -0,0 +1,3 @@
True - true
False - false
NullIsMissing - null
@@ -0,0 +1,8 @@
enum class MyEnum {
A, B, C
}
fun test(e: MyEnum?) {
<caret>when (e) {
}
}
@@ -0,0 +1,4 @@
EnumCheckIsMissing - MyEnum.A
EnumCheckIsMissing - MyEnum.B
EnumCheckIsMissing - MyEnum.C
NullIsMissing - null
@@ -0,0 +1 @@
unknown - else
@@ -0,0 +1,4 @@
fun test(n: Nothing?) {
<caret>when (n) {
}
}
@@ -0,0 +1 @@
NullIsMissing - null
@@ -0,0 +1,9 @@
sealed class Foo {
object A : Foo()
class B(val i: Int) : Foo()
}
fun test(e: Foo?) {
<caret>when (e) {
}
}
@@ -0,0 +1,3 @@
IsTypeCheckIsMissing - Foo.A
IsTypeCheckIsMissing - is Foo.B
NullIsMissing - null
@@ -0,0 +1,10 @@
sealed class Foo {
object A : Foo()
class B(val i: Int) : Foo()
}
fun test(e: Foo) {
<caret>when (e) {
else -> {}
}
}
@@ -0,0 +1,2 @@
IsTypeCheckIsMissing - Foo.A
IsTypeCheckIsMissing - is Foo.B
@@ -0,0 +1,9 @@
sealed class Foo {
object A : Foo()
class B(val i: Int) : Foo()
}
fun test(e: Foo) {
<caret>when (e) {
}
}
@@ -0,0 +1,2 @@
IsTypeCheckIsMissing - Foo.A
IsTypeCheckIsMissing - is Foo.B
@@ -0,0 +1,10 @@
sealed class Foo {
object A : Foo()
class B(val i: Int) : Foo()
}
fun test(e: Foo) {
<caret>when (e) {
Foo.A -> {}
}
}
@@ -0,0 +1 @@
IsTypeCheckIsMissing - is Foo.B
@@ -8,6 +8,14 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getResolveState
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.test.services.*
class ModuleRegistrarPreAnalysisHandler(
@@ -29,6 +37,24 @@ class ModuleRegistrarPreAnalysisHandler(
val project = testServices.compilerConfigurationProvider.getProject(testModule)
moduleInfoProvider.registerModuleInfo(project, testModule, ktFiles)
(project as MockProject).registerTestServices(testModule, allKtFiles, testServices)
// Manually process all inheritors of sealed classes so that SealedClassInheritorsProviderTestImpl can work correctly for tests.
// In the actual IDE, SealedClassInheritorsProviderIdeImpl works by finding inheritors from the index instead of do a
// preprocessing of all files. Therefore, the IDE does not rely on such a pre-analysis pass of all files in the module.
val projectStructureProvider = project.getService(ProjectStructureProvider::class.java)
val allFirFiles = mutableListOf<FirFile>()
ktFiles.forEach { (_, ktFile) ->
val ktModule = projectStructureProvider.getKtModuleForKtElement(ktFile)
val resolveState = ktModule.getResolveState(project)
allFirFiles.add(ktFile.getOrBuildFirFile(resolveState))
}
allFirFiles.groupBy { it.moduleData.session }.forEach { (session, firFiles) ->
for (firFile in firFiles) {
firFile.ensureResolved(FirResolvePhase.TYPES)
}
val sealedProcessor = FirSealedClassInheritorsProcessor(session, ScopeSession())
sealedProcessor.process(firFiles)
}
}
}
}
@@ -245,12 +245,12 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker()
) {
val flags = Flags()
whenExpression.accept(ConditionChecker, flags)
if (!flags.containsFalse) {
destination.add(WhenMissingCase.BooleanIsMissing.False)
}
if (!flags.containsTrue) {
destination.add(WhenMissingCase.BooleanIsMissing.True)
}
if (!flags.containsFalse) {
destination.add(WhenMissingCase.BooleanIsMissing.False)
}
}
private object ConditionChecker : AbstractConditionChecker<Flags>() {
@@ -5,8 +5,17 @@
package org.jetbrains.kotlin.generators.tests.analysis.api
import org.jetbrains.kotlin.analysis.api.descriptors.test.components.*
import org.jetbrains.kotlin.analysis.api.descriptors.test.symbols.AbstractKtFe10SymbolByFqNameTest
import org.jetbrains.kotlin.analysis.api.descriptors.test.symbols.AbstractKtFe10SymbolByReferenceTest
import org.jetbrains.kotlin.analysis.api.fir.AbstractFirReferenceResolveTest
import org.jetbrains.kotlin.analysis.api.fir.components.*
import org.jetbrains.kotlin.analysis.api.fir.scopes.AbstractFirDelegateMemberScopeTest
import org.jetbrains.kotlin.analysis.api.fir.scopes.AbstractFirFileScopeTest
import org.jetbrains.kotlin.analysis.api.fir.scopes.AbstractFirMemberScopeByFqNameTest
import org.jetbrains.kotlin.analysis.api.fir.symbols.AbstractFirSymbolByFqNameTest
import org.jetbrains.kotlin.analysis.api.fir.symbols.AbstractFirSymbolByPsiTest
import org.jetbrains.kotlin.analysis.api.fir.symbols.AbstractFirSymbolByReferenceTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostic.AbstractDiagnosticTraversalCounterTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostic.AbstractFirContextCollectionTest
@@ -14,23 +23,10 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostic.compiler.based
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostic.compiler.based.AbstractDiagnosisCompilerTestDataTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.AbstractFileStructureTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractInnerDeclarationsResolvePhaseTest
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
import org.jetbrains.kotlin.analysis.api.descriptors.test.components.*
import org.jetbrains.kotlin.analysis.api.descriptors.test.symbols.AbstractKtFe10SymbolByFqNameTest
import org.jetbrains.kotlin.analysis.api.descriptors.test.symbols.AbstractKtFe10SymbolByReferenceTest
import org.jetbrains.kotlin.analysis.api.fir.components.AbstractFirExpectedExpressionTypeTest
import org.jetbrains.kotlin.analysis.api.fir.components.AbstractFirHLExpressionTypeTest
import org.jetbrains.kotlin.analysis.api.fir.components.AbstractFirOverriddenDeclarationProviderTest
import org.jetbrains.kotlin.analysis.api.fir.components.AbstractFirRendererTest
import org.jetbrains.kotlin.analysis.api.fir.scopes.AbstractFirFileScopeTest
import org.jetbrains.kotlin.analysis.api.fir.scopes.AbstractFirMemberScopeByFqNameTest
import org.jetbrains.kotlin.analysis.api.fir.symbols.AbstractFirSymbolByFqNameTest
import org.jetbrains.kotlin.analysis.api.fir.symbols.AbstractFirSymbolByPsiTest
import org.jetbrains.kotlin.analysis.api.fir.symbols.AbstractFirSymbolByReferenceTest
import org.jetbrains.kotlin.analysis.api.fir.scopes.AbstractFirDelegateMemberScopeTest
import org.jetbrains.kotlin.spec.utils.GeneralConfiguration
import org.jetbrains.kotlin.spec.utils.tasks.detectDirsWithTestsMapFileOnly
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
import org.jetbrains.kotlin.test.runners.AbstractFirDiagnosticTestSpec
fun main(args: Array<String>) {
@@ -112,6 +108,10 @@ fun main(args: Array<String>) {
testClass<AbstractFirHLSmartCastInfoTest> {
model("components/smartCastInfo")
}
testClass<AbstractFirWhenMissingCasesTest> {
model("components/whenMissingCases")
}
}
testGroup("analysis/analysis-api-fe10/tests", "analysis/analysis-api/testData") {
@@ -170,6 +170,10 @@ fun main(args: Array<String>) {
testClass<AbstractKtFe10HLSmartCastInfoTest> {
model("components/smartCastInfo")
}
testClass<AbstractKtFe10WhenMissingCasesTest> {
model("components/whenMissingCases")
}
}
testGroup("analysis/low-level-api-fir/tests", "compiler/fir/raw-fir/psi2fir/testData") {