diff --git a/generators/sir-analysis-api-generator/build.gradle.kts b/generators/sir-analysis-api-generator/build.gradle.kts new file mode 100644 index 00000000000..a1b35e50665 --- /dev/null +++ b/generators/sir-analysis-api-generator/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +sourceSets { + "main" { java.srcDirs("main") } + "test" { projectDefault() } +} + +dependencies { + testApi(projectTests(":native:swift:sir-analysis-api")) + testImplementation(projectTests(":generators:test-generator")) + + testRuntimeOnly(projectTests(":analysis:analysis-test-framework")) + testImplementation(libs.junit.jupiter.api) +} + +val generateSirAnalysisApiTests by generator("org.jetbrains.kotlin.generators.tests.native.swift.sir.analysis.api.GenerateSirAnalysisApiTestsKt") + +testsJar() + +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xcontext-receivers") + } +} + + +projectTest(jUnitMode = JUnitMode.JUnit5) { + workingDir = rootDir + useJUnitPlatform { } +} diff --git a/generators/sir-analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/native/swift/sir/analysis/api/GenerateSirAnalysisApiTests.kt b/generators/sir-analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/native/swift/sir/analysis/api/GenerateSirAnalysisApiTests.kt new file mode 100644 index 00000000000..ef232f267d6 --- /dev/null +++ b/generators/sir-analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/native/swift/sir/analysis/api/GenerateSirAnalysisApiTests.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2023 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.generators.tests.native.swift.sir.analysis.api + +import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5 +import org.jetbrains.kotlin.sir.analysisapi.AbstractKotlinSirContextTest + + +fun main() { + System.setProperty("java.awt.headless", "true") + generateTestGroupSuiteWithJUnit5 { + testGroup( + "native/swift/sir-analysis-api/tests-gen/", + "native/swift/sir-analysis-api/testData" + ) { + testClass( + suiteTestClassName = "SirAnalysisGeneratedTests" + ) { + model("", pattern = "^([^_](.+)).kt$", recursive = false) + } + } + } +} diff --git a/native/swift/sir-analysis-api/README.md b/native/swift/sir-analysis-api/README.md new file mode 100644 index 00000000000..3984739bf70 --- /dev/null +++ b/native/swift/sir-analysis-api/README.md @@ -0,0 +1,29 @@ +# Build Swift IR from Analysis API + +This module is responsible for populating SIR tree. It is the first step in Swift Export pipeline. + +Input: +It should be possible to populate SIR from two types of artefacts: +1/ Kotlin Source Module +2/ KLib (currently not supported) + +## Dev guide + +### How to generate tests: +```bash +./gradlew :generators:sir-analysis-api-generator:generateSirAnalysisApiTests +``` +this will generate test by their input files. Input files could be found and should be placed here - `native/swift/sir-analysis-api/testData` + +The test expects to find `.sir` file, containing serialized SIR for the test-case. Name of the `.sir` file should be the same as a name of corresponding `.kt` file. + +The project for the generator can be found here - `generators/sir-analysis-api-generator/build.gradle.kts` + +### How to run tests: +```bash +./gradlew :native:swift:sir-analysis-api:test --tests "*" +``` +OR just open `SirAnalysisGeneratedTests` in IDEA and start them from gutter. + +### Project Setup +No additional setup required to develop this project. diff --git a/native/swift/sir-analysis-api/build.gradle.kts b/native/swift/sir-analysis-api/build.gradle.kts index 00be91114c4..9b9390c4a34 100644 --- a/native/swift/sir-analysis-api/build.gradle.kts +++ b/native/swift/sir-analysis-api/build.gradle.kts @@ -2,19 +2,45 @@ plugins { kotlin("jvm") } -description = "Build Swift IR from Analysis" +description = "Build Swift IR from Analysis API" dependencies { compileOnly(kotlinStdlib()) api(project(":native:swift:sir")) api(project(":analysis:analysis-api")) + implementation(project(":analysis:analysis-api-standalone")) - testImplementation(libs.junit4) + testApi(platform(libs.junit.bom)) + testRuntimeOnly(libs.junit.jupiter.engine) + testImplementation(libs.junit.jupiter.api) + + testRuntimeOnly(projectTests(":analysis:low-level-api-fir")) + testRuntimeOnly(projectTests(":analysis:analysis-api-impl-base")) + testImplementation(projectTests(":analysis:analysis-api-fir")) + testImplementation(projectTests(":analysis:analysis-test-framework")) testImplementation(projectTests(":compiler:tests-common")) + testImplementation(projectTests(":compiler:tests-common-new")) } sourceSets { "main" { projectDefault() } - "test" { projectDefault() } -} \ No newline at end of file + "test" { + projectDefault() + generatedTestDir() + } +} + +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xcontext-receivers") + } +} + + +projectTest(jUnitMode = JUnitMode.JUnit5) { + workingDir = rootDir + useJUnitPlatform { } +} + +testsJar() diff --git a/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt b/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt index 53e1f16564a..b596ff26204 100644 --- a/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt +++ b/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt @@ -5,11 +5,41 @@ package org.jetbrains.kotlin.sir.analysisapi -import org.jetbrains.kotlin.sir.SirElement +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtTreeVisitorVoid +import org.jetbrains.kotlin.psi.psiUtil.isPublic +import org.jetbrains.kotlin.sir.SirDeclaration +import org.jetbrains.kotlin.sir.SirForeignFunction /** * A root interface for classes that produce Swift IR elements. */ interface SirFactory { - fun build(): SirElement -} \ No newline at end of file + fun build(fromFile: KtFile): List +} + +class SirGenerator : SirFactory { + override fun build(fromFile: KtFile): List = analyze(fromFile) { + val res = mutableListOf() + fromFile.accept(Visitor(res)) + return res.toList() + } + + private class Visitor(val res: MutableList) : KtTreeVisitorVoid() { + override fun visitNamedFunction(function: KtNamedFunction) { + super.visitNamedFunction(function) + function + .takeIf { function.isPublic } + ?.fqName + ?.pathSegments() + ?.toListString() + ?.let { names -> SirForeignFunction(fqName = names) } + ?.let { res.add(it) } + } + } +} + +private fun List.toListString() = map { it.asString() } diff --git a/native/swift/sir-analysis-api/testData/functions_overload.kt b/native/swift/sir-analysis-api/testData/functions_overload.kt new file mode 100644 index 00000000000..9a950b1a926 --- /dev/null +++ b/native/swift/sir-analysis-api/testData/functions_overload.kt @@ -0,0 +1,12 @@ +// FILE: one.kt + +package namespace +fun foo(arg1: Int) = println("") +fun foo(arg1: Double) = println("") + +// FILE: two.kt + +package other.namespace + +fun foo(arg1: Int) = println("") +fun foo(arg1: Double) = println("") diff --git a/native/swift/sir-analysis-api/testData/functions_overload.sir b/native/swift/sir-analysis-api/testData/functions_overload.sir new file mode 100644 index 00000000000..bd3d511d335 --- /dev/null +++ b/native/swift/sir-analysis-api/testData/functions_overload.sir @@ -0,0 +1,4 @@ +[namespace, foo] +[namespace, foo] +[other, namespace, foo] +[other, namespace, foo] \ No newline at end of file diff --git a/native/swift/sir-analysis-api/testData/namespaced_functions.kt b/native/swift/sir-analysis-api/testData/namespaced_functions.kt new file mode 100644 index 00000000000..830744ee48e --- /dev/null +++ b/native/swift/sir-analysis-api/testData/namespaced_functions.kt @@ -0,0 +1,12 @@ +// FILE: one.kt + +package com.awesome.namespace +fun foo() = println("") + +fun bar() = "" + +// FILE: two.kt + +package com.awesome.other.namespace + +fun foo() = "" diff --git a/native/swift/sir-analysis-api/testData/namespaced_functions.sir b/native/swift/sir-analysis-api/testData/namespaced_functions.sir new file mode 100644 index 00000000000..c3958efaf87 --- /dev/null +++ b/native/swift/sir-analysis-api/testData/namespaced_functions.sir @@ -0,0 +1,3 @@ +[com, awesome, namespace, foo] +[com, awesome, namespace, bar] +[com, awesome, other, namespace, foo] \ No newline at end of file diff --git a/native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.kt b/native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.kt new file mode 100644 index 00000000000..d379c1f6ad2 --- /dev/null +++ b/native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.kt @@ -0,0 +1,3 @@ +public fun foo_public() = println("") +internal fun foo_internal() = println("") +private fun foo_private() = println("") diff --git a/native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.sir b/native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.sir new file mode 100644 index 00000000000..7981bbc97ee --- /dev/null +++ b/native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.sir @@ -0,0 +1 @@ +[foo_public] \ No newline at end of file diff --git a/native/swift/sir-analysis-api/testData/simple_function.kt b/native/swift/sir-analysis-api/testData/simple_function.kt new file mode 100644 index 00000000000..0d874d25131 --- /dev/null +++ b/native/swift/sir-analysis-api/testData/simple_function.kt @@ -0,0 +1 @@ +fun foo() = println("") diff --git a/native/swift/sir-analysis-api/testData/simple_function.sir b/native/swift/sir-analysis-api/testData/simple_function.sir new file mode 100644 index 00000000000..c8eb303be58 --- /dev/null +++ b/native/swift/sir-analysis-api/testData/simple_function.sir @@ -0,0 +1 @@ +[foo] \ No newline at end of file diff --git a/native/swift/sir-analysis-api/tests-gen/org/jetbrains/kotlin/sir/analysisapi/SirAnalysisGeneratedTests.java b/native/swift/sir-analysis-api/tests-gen/org/jetbrains/kotlin/sir/analysisapi/SirAnalysisGeneratedTests.java new file mode 100644 index 00000000000..4f08db2484c --- /dev/null +++ b/native/swift/sir-analysis-api/tests-gen/org/jetbrains/kotlin/sir/analysisapi/SirAnalysisGeneratedTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2023 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.sir.analysisapi; + +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 org.jetbrains.kotlin.generators.tests.native.swift.sir.analysis.api.GenerateSirAnalysisApiTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("native/swift/sir-analysis-api/testData") +@TestDataPath("$PROJECT_ROOT") +public class SirAnalysisGeneratedTests extends AbstractKotlinSirContextTest { + @Test + public void testAllFilesPresentInTestData() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/swift/sir-analysis-api/testData"), Pattern.compile("^([^_](.+)).kt$"), null, false); + } + + @Test + @TestMetadata("functions_overload.kt") + public void testFunctions_overload() throws Exception { + runTest("native/swift/sir-analysis-api/testData/functions_overload.kt"); + } + + @Test + @TestMetadata("namespaced_functions.kt") + public void testNamespaced_functions() throws Exception { + runTest("native/swift/sir-analysis-api/testData/namespaced_functions.kt"); + } + + @Test + @TestMetadata("should_ignore_not_public_functions.kt") + public void testShould_ignore_not_public_functions() throws Exception { + runTest("native/swift/sir-analysis-api/testData/should_ignore_not_public_functions.kt"); + } + + @Test + @TestMetadata("simple_function.kt") + public void testSimple_function() throws Exception { + runTest("native/swift/sir-analysis-api/testData/simple_function.kt"); + } +} diff --git a/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt b/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt new file mode 100644 index 00000000000..eec09e96e0f --- /dev/null +++ b/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2023 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.sir.analysisapi + +import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory +import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest +import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.* +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind +import org.jetbrains.kotlin.platform.konan.NativePlatforms +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.sir.SirForeignFunction +import org.jetbrains.kotlin.sir.SirModule +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.services.TestModuleStructure +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.assertions + +open class AbstractKotlinSirContextTest : AbstractKotlinSirContextTestBase() { + + override fun configureTest(builder: TestConfigurationBuilder) { + super.configureTest(builder) + with(builder) { + globalDefaults { + targetPlatform = NativePlatforms.unspecifiedNativePlatform + } + } + } + + override val configurator: AnalysisApiTestConfigurator + get() = AnalysisApiFirTestConfiguratorFactory.createConfigurator( + AnalysisApiTestConfiguratorFactoryData( + FrontendKind.Fir, + TestModuleKind.Source, + AnalysisSessionMode.Normal, + AnalysisApiMode.Ide + ) + ); +} + +abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest() { + override fun doTestByModuleStructure(moduleStructure: TestModuleStructure, testServices: TestServices) { + val ktFiles = moduleStructure.modules + .flatMap { testServices.ktModuleProvider.getModuleFiles(it).filterIsInstance() } + + val module = SirModule() + val sirFactory = SirGenerator() + ktFiles.forEach { file -> + module.declarations += sirFactory.build(file) + } + val actual = buildString { + module.declarations + .filterIsInstance() + .forEach { + appendLine("${it.fqName}") + } + } + + testServices.assertions.assertEqualsToTestDataFileSibling(actual, extension = ".sir") + } +} diff --git a/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/FactoryTests.kt b/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/FactoryTests.kt deleted file mode 100644 index 7934cbcdf31..00000000000 --- a/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/FactoryTests.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2010-2023 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.sir.analysisapi - -import org.jetbrains.kotlin.sir.SirElement -import org.junit.Test -import kotlin.test.assertEquals - -class FactoryTests { - @Test - fun smoke() { - val expectedString = "Element from factory" - val myFactory = object : SirFactory { - override fun build(): SirElement { - return object : SirElement { - override fun toString(): String { - return expectedString - } - } - } - } - val element = myFactory.build() - assertEquals(expectedString, element.toString()) - } -} \ No newline at end of file diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirElement.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirElement.kt index b98c9148d7d..285ce08b9a6 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirElement.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirElement.kt @@ -9,4 +9,13 @@ package org.jetbrains.kotlin.sir * A marker interface that denotes Swift IR elements. */ interface SirElement { -} \ No newline at end of file +} + +class SirModule : SirElement { + val declarations: MutableList = + mutableListOf() +} + +interface SirDeclaration + +class SirForeignFunction(val fqName: List) : SirDeclaration diff --git a/settings.gradle b/settings.gradle index 595fd4fbe20..bedd860d0f2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -400,7 +400,8 @@ include ":plugins:compose-compiler-plugin:compiler", // Swift Export modules include ":native:swift:sir", ":native:swift:sir-passes", - ":native:swift:sir-analysis-api" + ":native:swift:sir-analysis-api", + ":generators:sir-analysis-api-generator" void intellij(String imlPath) { File imlFile = new File("${rootDir}/intellij/community/plugins/kotlin/${imlPath}")