diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 4fa3a98c19..d0512e2012 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -8,6 +8,9 @@
+
+
+
diff --git a/artifacts.json b/artifacts.json
index 1026ffc690..5061907ff9 100644
--- a/artifacts.json
+++ b/artifacts.json
@@ -87,6 +87,22 @@
"packaging": "jar",
"javaVersion": "11"
},
+ {
+ "gradlePath": ":modulecheck-name:api",
+ "group": "com.rickbusarow.modulecheck",
+ "artifactId": "modulecheck-name-api",
+ "description": "Fast dependency graph linting for Gradle projects",
+ "packaging": "jar",
+ "javaVersion": "11"
+ },
+ {
+ "gradlePath": ":modulecheck-name:testing",
+ "group": "com.rickbusarow.modulecheck",
+ "artifactId": "modulecheck-name-testing",
+ "description": "Fast dependency graph linting for Gradle projects",
+ "packaging": "jar",
+ "javaVersion": "11"
+ },
{
"gradlePath": ":modulecheck-parsing:android",
"group": "com.rickbusarow.modulecheck",
@@ -343,6 +359,30 @@
"packaging": "jar",
"javaVersion": "11"
},
+ {
+ "gradlePath": ":modulecheck-parsing:element:api",
+ "group": "com.rickbusarow.modulecheck",
+ "artifactId": "modulecheck-parsing-element-api",
+ "description": "Fast dependency graph linting for Gradle projects",
+ "packaging": "jar",
+ "javaVersion": "11"
+ },
+ {
+ "gradlePath": ":modulecheck-parsing:element:impl-kotlin",
+ "group": "com.rickbusarow.modulecheck",
+ "artifactId": "modulecheck-parsing-element-kotlin",
+ "description": "Fast dependency graph linting for Gradle projects",
+ "packaging": "jar",
+ "javaVersion": "11"
+ },
+ {
+ "gradlePath": ":modulecheck-parsing:element:impl-resolve",
+ "group": "com.rickbusarow.modulecheck",
+ "artifactId": "modulecheck-parsing-element-impl-resolve",
+ "description": "Fast dependency graph linting for Gradle projects",
+ "packaging": "jar",
+ "javaVersion": "11"
+ },
{
"gradlePath": ":modulecheck-parsing:kotlin-compiler:api",
"group": "com.rickbusarow.modulecheck",
diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties
index 6e8a73fe44..e99cde2004 100644
--- a/build-logic/gradle.properties
+++ b/build-logic/gradle.properties
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-org.gradle.jvmargs=-Xmx12g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError
+org.gradle.jvmargs=-Xmx12g -XX:MaxMetaspaceSize=4g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.daemon=true
org.gradle.caching=true
org.gradle.parallel=true
diff --git a/detekt/detekt-config.yml b/detekt/detekt-config.yml
index 7d8a88a32d..d654f45b94 100644
--- a/detekt/detekt-config.yml
+++ b/detekt/detekt-config.yml
@@ -517,7 +517,7 @@ potential-bugs:
style:
active: true
AlsoCouldBeApply:
- active: false
+ active: true
BracesOnIfStatements:
active: true
singleLine: 'never'
@@ -527,12 +527,12 @@ style:
singleLine: 'necessary'
multiLine: 'consistent'
CanBeNonNullable:
- active: false
+ active: true
CascadingCallWrapping:
active: false
includeElvis: true
ClassOrdering:
- active: false
+ active: true
CollapsibleIfStatements:
active: false
DataClassContainsFunctions:
@@ -541,7 +541,7 @@ style:
- 'to'
allowOperators: false
DataClassShouldBeImmutable:
- active: false
+ active: true
DestructuringDeclarationWithTooManyEntries:
active: true
maxDestructuringEntries: 6
@@ -676,7 +676,7 @@ style:
NewLineAtEndOfFile:
active: true
NoTabs:
- active: false
+ active: true
NullableBooleanCheck:
active: false
ObjectLiteralToLambda:
@@ -686,7 +686,7 @@ style:
OptionalUnit:
active: false
PreferToOverPairSyntax:
- active: false
+ active: true
ProtectedMemberInFinalClass:
active: true
RedundantExplicitType:
diff --git a/gradle.properties b/gradle.properties
index 6e8a73fe44..e99cde2004 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-org.gradle.jvmargs=-Xmx12g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError
+org.gradle.jvmargs=-Xmx12g -XX:MaxMetaspaceSize=4g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.daemon=true
org.gradle.caching=true
org.gradle.parallel=true
diff --git a/modulecheck-config/fake/src/main/kotlin/modulecheck/config/fake/TestSettings.kt b/modulecheck-config/fake/src/main/kotlin/modulecheck/config/fake/TestSettings.kt
index 087039bf21..2a7fe4dc76 100644
--- a/modulecheck-config/fake/src/main/kotlin/modulecheck/config/fake/TestSettings.kt
+++ b/modulecheck-config/fake/src/main/kotlin/modulecheck/config/fake/TestSettings.kt
@@ -26,7 +26,7 @@ import modulecheck.config.ReportSettings
import modulecheck.config.ReportsSettings
import modulecheck.config.SortSettings
-@Suppress("UNUSED_PARAMETER")
+@Suppress("UNUSED_PARAMETER", "DataClassShouldBeImmutable")
data class TestSettings(
override var deleteUnused: Boolean = false,
override var trace: Boolean = false,
@@ -46,6 +46,7 @@ data class TestSettings(
fun sort(block: SortSettings.() -> Unit): Unit = Unit
}
+@Suppress("DataClassShouldBeImmutable")
data class TestChecksSettings(
override var redundantDependency: Boolean = ChecksSettings.REDUNDANT_DEPENDENCY_DEFAULT,
override var unusedDependency: Boolean = ChecksSettings.UNUSED_DEPENDENCY_DEFAULT,
@@ -62,6 +63,7 @@ data class TestChecksSettings(
override var depths: Boolean = ChecksSettings.DEPTHS_DEFAULT
) : ChecksSettings
+@Suppress("DataClassShouldBeImmutable")
data class TestSortSettings(
override var pluginComparators: List = SortSettings.PLUGIN_COMPARATORS_DEFAULT,
override var dependencyComparators: List = SortSettings.DEPENDENCY_COMPARATORS_DEFAULT
@@ -90,11 +92,13 @@ data class TestReportsSettings(
)
) : ReportsSettings
+@Suppress("DataClassShouldBeImmutable")
data class TestReportSettings(
override var enabled: Boolean,
override var outputPath: String
) : ReportSettings
+@Suppress("DataClassShouldBeImmutable")
data class TestPerModuleReportSettings(
override var enabled: Boolean,
override var outputDir: String?
diff --git a/modulecheck-core/src/test/kotlin/modulecheck/core/TextReportingTest.kt b/modulecheck-core/src/test/kotlin/modulecheck/core/TextReportingTest.kt
index 7ae247817e..6aea544bf9 100644
--- a/modulecheck-core/src/test/kotlin/modulecheck/core/TextReportingTest.kt
+++ b/modulecheck-core/src/test/kotlin/modulecheck/core/TextReportingTest.kt
@@ -37,6 +37,7 @@ internal class TextReportingTest : RunnerTest() {
fun `text report should not be created if disabled in settings`() = test {
settings.reports.text.enabled = false
+ settings.reports.text.outputPath = File(workingDir, settings.reports.text.outputPath).path
val outputFile = File(settings.reports.text.outputPath)
@@ -75,6 +76,7 @@ internal class TextReportingTest : RunnerTest() {
fun `text report should be created if enabled in settings`() = test {
settings.reports.text.enabled = true
+ settings.reports.text.outputPath = File(workingDir, settings.reports.text.outputPath).path
val outputFile = File(settings.reports.text.outputPath)
diff --git a/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/ModuleCheckExtension.kt b/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/ModuleCheckExtension.kt
index 90d22b048a..778ccfa4b0 100644
--- a/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/ModuleCheckExtension.kt
+++ b/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/ModuleCheckExtension.kt
@@ -58,6 +58,7 @@ import org.gradle.api.file.ProjectLayout
import org.gradle.api.model.ObjectFactory
import javax.inject.Inject
+@Suppress("ClassOrdering")
open class ModuleCheckExtension @Inject constructor(
objects: ObjectFactory,
projectLayout: ProjectLayout
@@ -185,6 +186,7 @@ open class SortExtension @Inject constructor(
.listProperty(DEPENDENCY_COMPARATORS_DEFAULT)
}
+@Suppress("ClassOrdering")
open class ReportsExtension @Inject constructor(
objects: ObjectFactory,
projectLayout: ProjectLayout
diff --git a/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/task/AbstractModuleCheckTask.kt b/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/task/AbstractModuleCheckTask.kt
index e82d030c79..6236a7a092 100644
--- a/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/task/AbstractModuleCheckTask.kt
+++ b/modulecheck-gradle/plugin/src/main/kotlin/modulecheck/gradle/task/AbstractModuleCheckTask.kt
@@ -44,8 +44,6 @@ abstract class AbstractModuleCheckTask(
}
}
- protected abstract fun ruleFilter(): RuleFilter
-
@get:Input
val settings: ModuleCheckExtension = project.extensions
.getByType(ModuleCheckExtension::class.java)
@@ -61,6 +59,8 @@ abstract class AbstractModuleCheckTask(
)
}
+ protected abstract fun ruleFilter(): RuleFilter
+
@TaskAction
fun run() {
try {
diff --git a/modulecheck-gradle/plugin/src/test/kotlin/modulecheck/gradle/McGradleTestEnvironment.kt b/modulecheck-gradle/plugin/src/test/kotlin/modulecheck/gradle/McGradleTestEnvironment.kt
index c04f6ca981..07b6b33b6d 100644
--- a/modulecheck-gradle/plugin/src/test/kotlin/modulecheck/gradle/McGradleTestEnvironment.kt
+++ b/modulecheck-gradle/plugin/src/test/kotlin/modulecheck/gradle/McGradleTestEnvironment.kt
@@ -143,6 +143,13 @@ class McGradleTestEnvironment(
.withProjectDir(workingDir)
}
+ constructor(params: GradleTestEnvironmentParams) : this(
+ testVersions = params.testVersions,
+ projectCache = params.projectCache,
+ testStackFrame = params.testStackFrame,
+ testVariantNames = params.testVariantNames
+ )
+
// Make sure that every project in the cache is also added to the root project's settings file
private fun addIncludes() {
val includes = projectCache.values.map { it.projectPath.value }
diff --git a/modulecheck-model/dependency/api/api/api.api b/modulecheck-model/dependency/api/api/api.api
index b3eb543ddf..a23631cf53 100644
--- a/modulecheck-model/dependency/api/api/api.api
+++ b/modulecheck-model/dependency/api/api/api.api
@@ -684,6 +684,22 @@ public final class modulecheck/model/dependency/SourceSets : java/util/Map, kotl
public final fun values ()Ljava/util/Collection;
}
+public final class modulecheck/model/dependency/SourceSetsKt {
+ public static final fun getAndroidTest (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getAndroidTestOrNull (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getDebug (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getDebugOrNull (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getMain (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getMainOrNull (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getRelease (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getReleaseOrNull (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getTest (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getTestFixtures (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getTestFixturesOrNull (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun getTestOrNull (Lmodulecheck/model/dependency/SourceSets;)Lmodulecheck/model/dependency/McSourceSet;
+ public static final fun requireSourceSet-wYXeOjA (Lmodulecheck/model/dependency/SourceSets;Ljava/lang/String;)Lmodulecheck/model/dependency/McSourceSet;
+}
+
public final class modulecheck/model/dependency/TransitiveProjectDependency {
public fun (Lmodulecheck/model/dependency/ProjectDependency;Lmodulecheck/model/dependency/ProjectDependency;)V
public final fun component1 ()Lmodulecheck/model/dependency/ProjectDependency;
diff --git a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/MavenCoordinates.kt b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/MavenCoordinates.kt
index b76632b99b..fac6705c71 100644
--- a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/MavenCoordinates.kt
+++ b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/MavenCoordinates.kt
@@ -42,19 +42,6 @@ data class MavenCoordinates(
override val name: String by unsafeLazy { "${group.orEmpty()}:$moduleName:${version.orEmpty()}" }
- companion object {
-
- private val MATCHER = """([\w\.]+):([\w\-]+):([\w\.]+)""".toRegex()
-
- fun parseOrNull(coordinateString: String): MavenCoordinates? {
- return MATCHER.find(coordinateString)
- ?.destructured
- ?.let { (group, moduleName, version) ->
- MavenCoordinates(group, moduleName, version)
- }
- }
- }
-
override fun compareTo(other: MavenCoordinates): Int {
return name.compareTo(other.name)
}
@@ -79,6 +66,19 @@ data class MavenCoordinates(
result = 31 * result + (version?.hashCode() ?: 0)
return result
}
+
+ companion object {
+
+ private val MATCHER = """([\w.]+):([\w\-]+):([\w.]+)""".toRegex()
+
+ fun parseOrNull(coordinateString: String): MavenCoordinates? {
+ return MATCHER.find(coordinateString)
+ ?.destructured
+ ?.let { (group, moduleName, version) ->
+ MavenCoordinates(group, moduleName, version)
+ }
+ }
+ }
}
sealed interface Identifier {
diff --git a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/McSourceSet.kt b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/McSourceSet.kt
index c6277c661b..1e7575590c 100644
--- a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/McSourceSet.kt
+++ b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/McSourceSet.kt
@@ -22,15 +22,6 @@ import modulecheck.utils.sequenceOfNotNull
import org.jetbrains.kotlin.config.JvmTarget
import java.io.File
-interface HasSourceSets {
- val sourceSets: SourceSets
-}
-
-/** Cache of [sourceSets][McSourceSet], probably at the project level. */
-class SourceSets(
- delegate: Map
-) : Map by delegate
-
/**
* Models all the particulars for a compilation unit, roughly equivalent
* to the source set models in AGP, KGP, and the Java Gradle Plugin.
@@ -119,15 +110,15 @@ class McSourceSet(
*/
val downstream: List by downstreamLazy
- fun withUpstream(): List = listOf(name) + upstream
- fun withDownstream(): List = listOf(name) + downstream
-
val hasExistingSourceFiles: Boolean by lazy {
jvmFiles.hasExistingFiles() ||
resourceFiles.hasExistingFiles() ||
layoutFiles.hasExistingFiles()
}
+ fun withUpstream(): List = listOf(name) + upstream
+ fun withDownstream(): List = listOf(name) + downstream
+
private fun Set.hasExistingFiles(): Boolean {
return any { dir ->
dir.walkBottomUp()
diff --git a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/ProjectPath.kt b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/ProjectPath.kt
index 41461f14a4..bd05794f48 100644
--- a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/ProjectPath.kt
+++ b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/ProjectPath.kt
@@ -93,6 +93,8 @@ sealed class ProjectPath : Identifier, Comparable {
}
}
+ abstract fun toTypeSafe(): TypeSafeProjectPath
+
companion object {
fun from(rawString: String): ProjectPath = if (rawString.trim().startsWith(':')) {
StringProjectPath(rawString)
@@ -100,8 +102,6 @@ sealed class ProjectPath : Identifier, Comparable {
TypeSafeProjectPath(rawString)
}
}
-
- abstract fun toTypeSafe(): TypeSafeProjectPath
}
internal val projectSplitRegex = "[.\\-_]".toRegex()
diff --git a/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/SourceSets.kt b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/SourceSets.kt
new file mode 100644
index 0000000000..32c9594a03
--- /dev/null
+++ b/modulecheck-model/dependency/api/src/main/kotlin/modulecheck/model/dependency/SourceSets.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.model.dependency
+
+import modulecheck.model.sourceset.SourceSetName
+import modulecheck.utils.requireNotNull
+
+/** Common interface for providing a [SourceSets] instance. */
+interface HasSourceSets {
+ val sourceSets: SourceSets
+}
+
+/** Cache of [sourceSets][McSourceSet], probably at the project level. */
+class SourceSets(
+ delegate: Map
+) : Map by delegate
+
+/**
+ * shorthand for the source set associated with 'androidTest'.
+ * @throws IllegalArgumentException if there is no such source set
+ */
+val SourceSets.androidTest: McSourceSet get() = requireSourceSet(SourceSetName.ANDROID_TEST)
+
+/** shorthand for `get("androidTest".asSourceSetName())` */
+val SourceSets.androidTestOrNull: McSourceSet? get() = get(SourceSetName.ANDROID_TEST)
+
+/**
+ * shorthand for the source set associated with 'debug'.
+ * @throws IllegalArgumentException if there is no such source set
+ */
+val SourceSets.debug: McSourceSet get() = requireSourceSet(SourceSetName.DEBUG)
+
+/** shorthand for `get("debug".asSourceSetName())` */
+val SourceSets.debugOrNull: McSourceSet? get() = get(SourceSetName.DEBUG)
+
+/**
+ * shorthand for the source set associated with 'main'.
+ * @throws IllegalArgumentException if there is no such source set
+ */
+val SourceSets.main: McSourceSet get() = requireSourceSet(SourceSetName.MAIN)
+
+/** shorthand for `get("main".asSourceSetName())` */
+val SourceSets.mainOrNull: McSourceSet? get() = get(SourceSetName.MAIN)
+
+/**
+ * shorthand for the source set associated with 'release'.
+ * @throws IllegalArgumentException if there is no such source set
+ */
+val SourceSets.release: McSourceSet get() = requireSourceSet(SourceSetName.RELEASE)
+
+/** shorthand for `get("release".asSourceSetName())` */
+val SourceSets.releaseOrNull: McSourceSet? get() = get(SourceSetName.RELEASE)
+
+/**
+ * shorthand for the source set associated with 'test'.
+ * @throws IllegalArgumentException if there is no such source set
+ */
+val SourceSets.test: McSourceSet get() = requireSourceSet(SourceSetName.TEST)
+
+/** shorthand for `get("test".asSourceSetName())` */
+val SourceSets.testOrNull: McSourceSet? get() = get(SourceSetName.TEST)
+
+/**
+ * shorthand for the source set associated with 'testFixtures'.
+ * @throws IllegalArgumentException if there is no such source set
+ */
+val SourceSets.testFixtures: McSourceSet get() = requireSourceSet(SourceSetName.TEST_FIXTURES)
+
+/** shorthand for `get("testFixtures".asSourceSetName())` */
+val SourceSets.testFixturesOrNull: McSourceSet? get() = get(SourceSetName.TEST_FIXTURES)
+
+/**
+ * @return the source set associated with [sourceSetName], or throws if there is no value
+ * @throws IllegalArgumentException if this collection
+ * does not contain a source set for the requested name
+ */
+fun SourceSets.requireSourceSet(sourceSetName: SourceSetName): McSourceSet {
+ return get(sourceSetName).requireNotNull {
+ val simpleName = SourceSets::class.java.simpleName
+ "This $simpleName instance does not have a value for ${sourceSetName.value}. " +
+ "The existing keys are: ${keys.map { it.value }}"
+ }
+}
diff --git a/modulecheck-name/api/api/api.api b/modulecheck-name/api/api/api.api
new file mode 100644
index 0000000000..818063e54a
--- /dev/null
+++ b/modulecheck-name/api/api/api.api
@@ -0,0 +1,232 @@
+public final class modulecheck/name/AndroidDataBindingName : modulecheck/name/AndroidName, modulecheck/name/NameWithPackageName {
+ public synthetic fun (Ljava/lang/String;Lmodulecheck/name/AndroidResourceName;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun getPackageName-A9AyiEE ()Ljava/lang/String;
+ public fun getSimpleNames ()Ljava/util/List;
+}
+
+public abstract interface class modulecheck/name/AndroidName : modulecheck/name/HasSimpleNames, modulecheck/name/Name {
+ public static final field Companion Lmodulecheck/name/AndroidName$Companion;
+}
+
+public final class modulecheck/name/AndroidName$Companion {
+ public final fun dataBinding-9PpwmVA (Lmodulecheck/name/UnqualifiedAndroidResourceName;Ljava/lang/String;)Lmodulecheck/name/AndroidDataBindingName;
+ public final fun qualifiedAndroidResource (Lmodulecheck/name/AndroidRName;Lmodulecheck/name/UnqualifiedAndroidResourceName;)Lmodulecheck/name/AndroidResourceNameWithRName;
+ public final fun r-p04zEWQ (Ljava/lang/String;)Lmodulecheck/name/AndroidRName;
+}
+
+public final class modulecheck/name/AndroidRName : modulecheck/name/AndroidName, modulecheck/name/NameWithPackageName {
+ public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun getPackageName-A9AyiEE ()Ljava/lang/String;
+ public fun getSimpleNames ()Ljava/util/List;
+}
+
+public abstract interface class modulecheck/name/AndroidResourceName : modulecheck/name/AndroidName, modulecheck/name/HasSimpleNames, modulecheck/name/Name {
+ public abstract fun getIdentifier-yyTc5LE ()Ljava/lang/String;
+ public abstract fun getPrefix-yyTc5LE ()Ljava/lang/String;
+}
+
+public final class modulecheck/name/AndroidResourceNameWithRName : modulecheck/name/AndroidResourceName, modulecheck/name/NameWithPackageName {
+ public fun (Lmodulecheck/name/AndroidRName;Lmodulecheck/name/UnqualifiedAndroidResourceName;)V
+ public final fun getAndroidRName ()Lmodulecheck/name/AndroidRName;
+ public fun getIdentifier-yyTc5LE ()Ljava/lang/String;
+ public fun getPackageName-A9AyiEE ()Ljava/lang/String;
+ public fun getPrefix-yyTc5LE ()Ljava/lang/String;
+ public final fun getResourceName ()Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public fun getSimpleNames ()Ljava/util/List;
+}
+
+public final class modulecheck/name/ClassName : modulecheck/name/NameWithPackageName, modulecheck/name/TypeName {
+ public synthetic fun (Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Ljava/lang/String;[Ljava/lang/String;Ljava/util/List;Z)V
+ public synthetic fun (Ljava/lang/String;[Ljava/lang/String;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1-A9AyiEE ()Ljava/lang/String;
+ public final fun component2 ()Ljava/util/List;
+ public final fun component3 ()Ljava/util/List;
+ public final fun component4 ()Z
+ public final fun copy-XtOWetg (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)Lmodulecheck/name/ClassName;
+ public static synthetic fun copy-XtOWetg$default (Lmodulecheck/name/ClassName;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILjava/lang/Object;)Lmodulecheck/name/ClassName;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getAsStringWithTypeParameters ()Ljava/lang/String;
+ public fun getNullable ()Z
+ public fun getPackageName-A9AyiEE ()Ljava/lang/String;
+ public fun getSimpleNames ()Ljava/util/List;
+ public final fun getTypeArguments ()Ljava/util/List;
+ public fun hashCode ()I
+ public fun makeNotNullable ()Lmodulecheck/name/TypeName;
+ public fun makeNullable ()Lmodulecheck/name/TypeName;
+ public final fun parameterizedBy ([Lmodulecheck/name/TypeName;)Lmodulecheck/name/ClassName;
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class modulecheck/name/HasNameSegments {
+ public abstract fun getSegments ()Ljava/util/List;
+}
+
+public abstract interface class modulecheck/name/HasPackageName {
+ public abstract fun getPackageName-A9AyiEE ()Ljava/lang/String;
+}
+
+public abstract interface class modulecheck/name/HasSimpleNames : modulecheck/name/HasNameSegments {
+ public static final field Companion Lmodulecheck/name/HasSimpleNames$Companion;
+ public fun getSegments ()Ljava/util/List;
+ public abstract fun getSimpleNames ()Ljava/util/List;
+ public fun getSimplestName-yyTc5LE ()Ljava/lang/String;
+}
+
+public final class modulecheck/name/HasSimpleNames$Companion {
+}
+
+public abstract interface class modulecheck/name/Name : java/io/Serializable, java/lang/Comparable {
+ public synthetic fun compareTo (Ljava/lang/Object;)I
+ public fun compareTo (Lmodulecheck/name/Name;)I
+ public abstract fun getAsString ()Ljava/lang/String;
+ public fun getSimpleName-yyTc5LE ()Ljava/lang/String;
+ public fun getSimpleNameString ()Ljava/lang/String;
+}
+
+public abstract interface class modulecheck/name/NameWithPackageName : modulecheck/name/HasPackageName, modulecheck/name/HasSimpleNames, modulecheck/name/ResolvableName {
+ public static final field Companion Lmodulecheck/name/NameWithPackageName$Companion;
+ public fun getAsString ()Ljava/lang/String;
+ public fun getSegments ()Ljava/util/List;
+ public fun isTopLevel ()Z
+}
+
+public final class modulecheck/name/NameWithPackageName$Companion {
+ public final fun invoke-Q6FKjUA (Ljava/lang/String;Ljava/util/List;)Lmodulecheck/name/NameWithPackageName;
+}
+
+public final class modulecheck/name/NameWithPackageNameKt {
+ public static final fun asNameWithPackageName-23wzK4s (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static final fun asNameWithPackageName-9PpwmVA (Ljava/lang/Iterable;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static final fun asNameWithPackageName-9PpwmVA (Lorg/jetbrains/kotlin/name/FqName;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+}
+
+public final class modulecheck/name/PackageName : modulecheck/name/HasNameSegments, modulecheck/name/Name {
+ public static final field Companion Lmodulecheck/name/PackageName$Companion;
+ public static final fun append-f585xGA (Ljava/lang/String;Ljava/lang/Iterable;)Ljava/lang/String;
+ public static final fun appendAsString-impl (Ljava/lang/String;Ljava/lang/Iterable;)Ljava/lang/String;
+ public static final synthetic fun box-impl (Ljava/lang/String;)Lmodulecheck/name/PackageName;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
+ public fun getAsString ()Ljava/lang/String;
+ public fun getSegments ()Ljava/util/List;
+ public static fun getSegments-impl (Ljava/lang/String;)Ljava/util/List;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/String;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/String;
+}
+
+public final class modulecheck/name/PackageName$Companion {
+ public final fun asPackageName-f585xGA (Ljava/lang/String;)Ljava/lang/String;
+ public final fun getDEFAULT-A9AyiEE ()Ljava/lang/String;
+ public final fun invoke-f585xGA (Ljava/lang/String;)Ljava/lang/String;
+}
+
+public final class modulecheck/name/PackageNameKt {
+ public static final fun append-Q6FKjUA (Ljava/lang/String;Ljava/lang/Iterable;)Ljava/lang/String;
+ public static final fun appendAsString-Q6FKjUA (Ljava/lang/String;Ljava/lang/Iterable;)Ljava/lang/String;
+ public static final fun appendAsString-Q6FKjUA (Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/String;
+}
+
+public abstract interface class modulecheck/name/ResolvableName : modulecheck/name/Name {
+}
+
+public final class modulecheck/name/SimpleName : modulecheck/name/Name {
+ public static final field Companion Lmodulecheck/name/SimpleName$Companion;
+ public static final synthetic fun box-impl (Ljava/lang/String;)Lmodulecheck/name/SimpleName;
+ public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
+ public fun getAsString ()Ljava/lang/String;
+ public fun getSimpleName-yyTc5LE ()Ljava/lang/String;
+ public static fun getSimpleName-yyTc5LE (Ljava/lang/String;)Ljava/lang/String;
+ public fun getSimpleNameString ()Ljava/lang/String;
+ public static fun getSimpleNameString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/String;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/String;
+}
+
+public final class modulecheck/name/SimpleName$Companion {
+ public final fun asSimpleName-d3qGUlM (Ljava/lang/String;)Ljava/lang/String;
+ public final fun asString (Ljava/util/List;)Ljava/lang/String;
+ public final fun getJAVA_IDENTIFIER_REGEX ()Lkotlin/text/Regex;
+ public final fun getSIMPLE_NAME_REGEX ()Lkotlin/text/Regex;
+ public final fun stripPackageNameFromFqName-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
+}
+
+public abstract interface class modulecheck/name/TypeName : modulecheck/name/HasSimpleNames, modulecheck/name/Name {
+ public abstract fun getNullable ()Z
+ public abstract fun makeNotNullable ()Lmodulecheck/name/TypeName;
+ public abstract fun makeNullable ()Lmodulecheck/name/TypeName;
+}
+
+public final class modulecheck/name/TypeParameter : modulecheck/name/TypeName {
+ public synthetic fun (Ljava/lang/String;Ljava/util/List;ZLmodulecheck/name/TypeParameter$Variance;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Ljava/lang/String;[Lmodulecheck/name/TypeName;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1-yyTc5LE ()Ljava/lang/String;
+ public final fun component2 ()Ljava/util/List;
+ public final fun component3 ()Z
+ public final fun component4 ()Lmodulecheck/name/TypeParameter$Variance;
+ public final fun copy-PwtOO7w (Ljava/lang/String;Ljava/util/List;ZLmodulecheck/name/TypeParameter$Variance;)Lmodulecheck/name/TypeParameter;
+ public static synthetic fun copy-PwtOO7w$default (Lmodulecheck/name/TypeParameter;Ljava/lang/String;Ljava/util/List;ZLmodulecheck/name/TypeParameter$Variance;ILjava/lang/Object;)Lmodulecheck/name/TypeParameter;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getAsString ()Ljava/lang/String;
+ public final fun getBounds ()Ljava/util/List;
+ public fun getNullable ()Z
+ public fun getSegments ()Ljava/util/List;
+ public fun getSimpleName-yyTc5LE ()Ljava/lang/String;
+ public fun getSimpleNames ()Ljava/util/List;
+ public final fun getVariance ()Lmodulecheck/name/TypeParameter$Variance;
+ public fun hashCode ()I
+ public fun makeNotNullable ()Lmodulecheck/name/TypeName;
+ public fun makeNullable ()Lmodulecheck/name/TypeName;
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/name/TypeParameter$Variance : java/lang/Enum {
+ public static final field IN Lmodulecheck/name/TypeParameter$Variance;
+ public static final field OUT Lmodulecheck/name/TypeParameter$Variance;
+ public static fun valueOf (Ljava/lang/String;)Lmodulecheck/name/TypeParameter$Variance;
+ public static fun values ()[Lmodulecheck/name/TypeParameter$Variance;
+}
+
+public final class modulecheck/name/UnqualifiedAndroidResourceName : modulecheck/name/AndroidResourceName {
+ public static final field Companion Lmodulecheck/name/UnqualifiedAndroidResourceName$Companion;
+ public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun getAsString ()Ljava/lang/String;
+ public fun getIdentifier-yyTc5LE ()Ljava/lang/String;
+ public fun getPrefix-yyTc5LE ()Ljava/lang/String;
+ public fun getSegments ()Ljava/util/List;
+ public fun getSimpleNames ()Ljava/util/List;
+ public final fun toAndroidResourceNameWithRName (Lmodulecheck/name/AndroidRName;)Lmodulecheck/name/AndroidResourceNameWithRName;
+}
+
+public final class modulecheck/name/UnqualifiedAndroidResourceName$Companion {
+ public final fun anim-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun animator-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun array-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun bool-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun color-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun dimen-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun drawable-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun font-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun fromFile (Ljava/io/File;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun fromValuePair (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun fromXmlString (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun id-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun integer-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun layout-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun menu-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun mipmap-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun raw-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun string-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+ public final fun style-tQbDKes (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+}
+
diff --git a/modulecheck-name/api/build.gradle.kts b/modulecheck-name/api/build.gradle.kts
new file mode 100644
index 0000000000..304df3c530
--- /dev/null
+++ b/modulecheck-name/api/build.gradle.kts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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.
+ */
+
+plugins {
+ id("mcbuild")
+}
+
+mcbuild {
+ published(
+ artifactId = "modulecheck-name-api"
+ )
+}
+
+dependencies {
+
+ api(libs.kotlin.compiler)
+ api(libs.kotlinx.coroutines.core)
+ api(libs.kotlinx.coroutines.jvm)
+ api(libs.square.kotlinPoet)
+
+ implementation(project(path = ":modulecheck-utils:lazy"))
+ implementation(project(path = ":modulecheck-utils:stdlib"))
+
+ testImplementation(libs.bundles.junit)
+ testImplementation(libs.bundles.kotest)
+
+ testImplementation(project(path = ":modulecheck-internal-testing"))
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidDataBindingName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidDataBindingName.kt
new file mode 100644
index 0000000000..ff5edf69da
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidDataBindingName.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.HasSimpleNames.Companion.checkSimpleNames
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import modulecheck.utils.capitalize
+import modulecheck.utils.lazy.unsafeLazy
+
+/** example: `com.example.databinding.FragmentListBinding` */
+class AndroidDataBindingName(
+ override val packageName: PackageName,
+ sourceLayout: AndroidResourceName
+) : NameWithPackageName, AndroidName {
+
+ override val simpleNames: List by unsafeLazy {
+
+ val simpleBindingName = sourceLayout.identifier.asString
+ .split("_")
+ .joinToString("") { it.capitalize() }
+ .plus("Binding")
+ .asSimpleName()
+
+ listOf(
+ "databinding".asSimpleName(),
+ simpleBindingName
+ )
+ }
+
+ init {
+ checkSimpleNames()
+ }
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidName.kt
new file mode 100644
index 0000000000..42279d2181
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidName.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.SimpleName.Companion.asSimpleName
+
+/**
+ * - fully qualified generated resources like `com.example.R.string.app_name`
+ * - generated data-/view-binding declarations like `com.example.databinding.FragmentListBinding`
+ * - unqualified resources which can be consumed in downstream projects, like `R.string.app_name`
+ * - R names, like `com.example.R`
+ */
+sealed interface AndroidName : Name, HasSimpleNames {
+
+ companion object {
+ /** @return example: `com.example.app.R` */
+ fun r(packageName: PackageName): AndroidRName = AndroidRName(packageName)
+
+ /** @return `com.example.R.string.app_name` */
+ fun qualifiedAndroidResource(
+ sourceR: AndroidRName,
+ sourceResource: UnqualifiedAndroidResourceName
+ ): AndroidResourceNameWithRName = AndroidResourceNameWithRName(
+ androidRName = sourceR,
+ resourceName = sourceResource
+ )
+
+ /** @return `com.example.databinding.FragmentListBinding` */
+ fun dataBinding(
+ sourceLayout: UnqualifiedAndroidResourceName,
+ packageName: PackageName
+ ): AndroidDataBindingName = AndroidDataBindingName(
+ packageName = packageName,
+ sourceLayout = sourceLayout
+ )
+ }
+}
+
+/** example: `com.example.app.R` */
+class AndroidRName(
+ override val packageName: PackageName
+) : NameWithPackageName, AndroidName {
+
+ override val simpleNames: List by lazy { listOf("R".asSimpleName()) }
+}
+
+/**
+ * Models fully qualified names like `com.example.R.string.app_name`
+ * or unqualified ones like `string.app_name`.
+ */
+sealed interface AndroidResourceName : AndroidName, Name, HasSimpleNames {
+ /** example: 'string' in `R.string.app_name` */
+ val prefix: SimpleName
+
+ /** example: 'app_name' in `R.string.app_name` */
+ val identifier: SimpleName
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidResourceNameWithRName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidResourceNameWithRName.kt
new file mode 100644
index 0000000000..60bd358995
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/AndroidResourceNameWithRName.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.utils.lazy.unsafeLazy
+
+/**
+ * example: `com.example.R.string.app_name`
+ *
+ * @property androidRName the R declaration used when AGP generates this fully qualified resource
+ * @property resourceName the resource declaration, like `string.app_name`
+ */
+class AndroidResourceNameWithRName(
+ val androidRName: AndroidRName,
+ val resourceName: UnqualifiedAndroidResourceName
+) : NameWithPackageName, AndroidResourceName {
+ override val packageName: PackageName
+ get() = androidRName.packageName
+
+ override val simpleNames: List by unsafeLazy {
+ androidRName.simpleNames + resourceName.simpleNames
+ }
+ override val prefix: SimpleName get() = resourceName.prefix
+ override val identifier: SimpleName get() = resourceName.identifier
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/Name.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/Name.kt
new file mode 100644
index 0000000000..c23ce412fd
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/Name.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import java.io.Serializable
+
+/**
+ * Fundamentally, this is a version of `Name` or `FqName` (such as Kotlin's
+ * [Name][org.jetbrains.kotlin.name.Name] and [FqName][org.jetbrains.kotlin.name.FqName])
+ * with syntactic sugar for complex matching requirements.
+ */
+sealed interface Name : Comparable, Serializable {
+
+ /** The raw String value of this name, such as `com.example.lib1.Lib1Class`. */
+ val asString: String
+
+ /** The simplest name. For an inner class like `com.example.Outer.Inner`, this will be 'Inner'. */
+ val simpleName: SimpleName
+ get() = (this as? HasSimpleNames)?.simpleNames?.last()
+ ?: asString.split('.').last().asSimpleName()
+
+ /** The simplest name. For an inner class like `com.example.Outer.Inner`, this will be 'Inner'. */
+ val simpleNameString: String
+ get() = simpleName.asString
+
+ override fun compareTo(other: Name): Int {
+ return compareValuesBy(this, other, { it.asString }, { it::class.qualifiedName })
+ }
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/NameWithPackageName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/NameWithPackageName.kt
new file mode 100644
index 0000000000..89dd7b8cec
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/NameWithPackageName.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.HasSimpleNames.Companion.checkSimpleNames
+import modulecheck.name.SimpleName.Companion.stripPackageNameFromFqName
+import modulecheck.utils.asList
+import modulecheck.utils.lazy.unsafeLazy
+import modulecheck.utils.singletonList
+import org.jetbrains.kotlin.name.FqName
+
+/** Represents a "declaration" -- a named object which can be referenced elsewhere. */
+sealed interface NameWithPackageName :
+ HasPackageName,
+ HasSimpleNames,
+ ResolvableName {
+
+ override val asString: String
+ get() = packageName.appendAsString(simpleNames)
+
+ override val segments: List
+ get() = asString.split('.')
+
+ /**
+ * `true` if a declaration is top-level in a file, otherwise `false`
+ * such as if the declaration is a nested type or a member declaration
+ */
+ val isTopLevel: Boolean
+ get() = simpleNames.size == 1
+
+ companion object {
+ /** */
+ operator fun invoke(
+ packageName: PackageName,
+ simpleNames: List
+ ): NameWithPackageName = NameWithPackageNameImpl(packageName, simpleNames)
+ }
+}
+
+internal data class NameWithPackageNameImpl(
+ override val packageName: PackageName,
+ override val simpleNames: List
+) : NameWithPackageName {
+ init {
+ checkSimpleNames()
+ }
+
+ override val segments: List by unsafeLazy { asString.split('.') }
+}
+
+/**
+ * @return a [NameWithPackageName], where the String after [packageName]
+ * is split and treated as the collection of [SimpleNames][SimpleName].
+ */
+fun FqName.asNameWithPackageName(packageName: PackageName): NameWithPackageName = asString()
+ .stripPackageNameFromFqName(packageName)
+ .asNameWithPackageName(packageName)
+
+/**
+ * @return a [NameWithPackageName] from the [packageName]
+ * argument, appending the receiver [SimpleNames][SimpleName]
+ */
+fun Iterable.asNameWithPackageName(packageName: PackageName): NameWithPackageName {
+ return NameWithPackageNameImpl(packageName, this.asList())
+}
+
+/**
+ * @return a [NameWithPackageName] from the [packageName]
+ * argument, appending the receiver [SimpleNames][SimpleName]
+ */
+fun SimpleName.asNameWithPackageName(packageName: PackageName): NameWithPackageName {
+ return singletonList().asNameWithPackageName(packageName)
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/PackageName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/PackageName.kt
new file mode 100644
index 0000000000..7c634d605c
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/PackageName.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.PackageName.Companion.asPackageName
+import modulecheck.name.SimpleName.Companion.asSimpleName
+
+/**
+ * Represents a package name.
+ *
+ * Note that a java/kotlin file without a package declaration will have a `null` _declaration_, but
+ * it still has a "default" package. Files with a default package should use [PackageName.DEFAULT].
+ *
+ * @property asString the full name of this package
+ * @see Name
+ * @throws IllegalArgumentException if the [asString] parameter is empty or blank
+ */
+@JvmInline
+value class PackageName private constructor(
+ override val asString: String
+) : Name, HasNameSegments {
+
+ override val segments: List
+ get() = asString.split('.')
+
+ /**
+ * Safe function for appending a simple name to the "end" of a package name.
+ *
+ * If the package name is default/empty, this function will
+ * return just the simple name without a preceding period.
+ *
+ * If the package name is not blank, this function will append
+ * a period to the package name, then add the simple name.
+ */
+ fun appendAsString(simpleNames: Iterable): String {
+ return "$asString.${simpleNames.joinToString(".") { it.asString }}"
+ }
+
+ /**
+ * Safe function for appending a simple name to the "end" of a package name.
+ *
+ * If the package name is default/empty, this function will
+ * return just the simple name without a preceding period.
+ *
+ * If the package name is not blank, this function will append
+ * a period to the package name, then add the simple name.
+ */
+ fun append(simpleNames: Iterable): PackageName {
+ return appendAsString(simpleNames).asPackageName()
+ }
+
+ companion object {
+
+ /**
+ * Represents a [PackageName] when there isn't actually a package name, meaning that
+ * top-level declarations in that file are at the root of source without qualifiers.
+ *
+ * @see Name
+ * @see DEFAULT
+ */
+ val DEFAULT: PackageName = PackageName("")
+
+ /** Shorthand for calling [PackageName.invoke] in-line. */
+ fun String?.asPackageName(): PackageName = PackageName(this)
+
+ /**
+ * Shorthand for calling [PackageName.invoke] in-line.
+ *
+ * @return A `PackageName` wrapper around [nameOrNull]. If [nameOrNull]
+ * is null or blank, this will return [PackageName.DEFAULT].
+ */
+ operator fun invoke(nameOrNull: String?): PackageName {
+ return when {
+ nameOrNull.isNullOrBlank() -> DEFAULT
+
+ else -> PackageName(nameOrNull)
+ }
+ }
+ }
+}
+
+/** Convenience interface for providing a [PackageName]. */
+interface HasPackageName {
+ val packageName: PackageName
+}
+
+/**
+ * Safe function for appending a simple name to the "end" of a package name.
+ *
+ * If the package name is default/empty, this function will
+ * return just the simple name without a preceding period.
+ *
+ * If the package name is not blank, this function will append
+ * a period to the package name, then add the simple name.
+ */
+fun PackageName.appendAsString(simpleNames: Iterable): String {
+ return appendAsString(simpleNames.map { it.asSimpleName() })
+}
+
+/**
+ * Safe function for appending a simple name to the "end" of a package name.
+ *
+ * If the package name is default/empty, this function will
+ * return just the simple name without a preceding period.
+ *
+ * If the package name is not blank, this function will append
+ * a period to the package name, then add the simple name.
+ */
+fun PackageName.append(simpleNames: Iterable): PackageName {
+ return appendAsString(simpleNames).asPackageName()
+}
+
+/**
+ * Safe function for appending a simple name to the "end" of a package name.
+ *
+ * If the package name is default/empty, this function will
+ * return just the simple name without a preceding period.
+ *
+ * If the package name is not blank, this function will append
+ * a period to the package name, then add the simple name.
+ */
+fun PackageName.appendAsString(vararg simpleNames: String): String {
+ return appendAsString(simpleNames.toList())
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/ResolvableName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/ResolvableName.kt
new file mode 100644
index 0000000000..38c8795a6c
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/ResolvableName.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+/** An [Name] which has the potential to be resolved -- meaning any [NameWithPackageName] */
+sealed interface ResolvableName : Name
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/SimpleName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/SimpleName.kt
new file mode 100644
index 0000000000..20864d3d8a
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/SimpleName.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.utils.regex
+
+/**
+ * A name which is not fully qualified, like `Foo` in `com.example.Foo`
+ *
+ * @property asString the string value of this name
+ */
+@JvmInline
+value class SimpleName(override val asString: String) : Name {
+
+ init {
+ require(asString.matches(SIMPLE_NAME_REGEX)) {
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$asString`"
+ }
+ }
+
+ override val simpleName: SimpleName
+ get() = this
+
+ override val simpleNameString: String
+ get() = asString
+
+ companion object {
+
+ /** matches simple type or member names */
+ val JAVA_IDENTIFIER_REGEX: Regex = regex {
+
+ appendWithoutInjection("(?:")
+
+ // matches any alphabet character or an underscore at the start of a word
+ append("""\b[_a-zA-Z]""")
+
+ or()
+
+ // matches the literal '$' character anywhere but the start of a word
+ append("""\B\$""")
+
+ appendWithoutInjection(")")
+
+ // matches any alphabet character, digit, underscore, or literal '$' character
+ append("""[_a-zA-Z0-9$]*+""")
+ }
+
+ /** Basic validation for simple names. They must not include any periods. */
+ val SIMPLE_NAME_REGEX: Regex = regex {
+
+ append("^")
+
+ // any normal identifier not wrapped in backticks
+ append(JAVA_IDENTIFIER_REGEX.pattern)
+
+ or()
+
+ // matches names wrapped in backticks, stopping at the first backtick character.
+ // This is intentionally lenient,
+ // because the set of characters Kotlin allows is dependent upon the platform.
+ append("""`[^\n`]+`""")
+
+ append("$")
+ }
+
+ /** shorthand for `joinToString(".") { it.name.trim() }` */
+ fun List.asString(): String = joinToString(".") { it.asString.trim() }
+
+ /** wraps this String in a [SimpleName] */
+ fun String.asSimpleName(): SimpleName = SimpleName(this)
+
+ /**
+ * Removes the prefix of [packageName]'s value and a subsequent period,
+ * then splits the remainder by dots and returns that list as [SimpleName]
+ *
+ * example: `com.example.Outer.Inner` becomes `[Outer, Inner]`
+ */
+ fun String.stripPackageNameFromFqName(packageName: PackageName): List {
+ return removePrefix("${packageName.asString}.").split('.')
+ .map { it.asSimpleName() }
+ }
+ }
+}
+
+/** Convenience interface for providing a [SimpleName]. */
+interface HasSimpleNames : HasNameSegments {
+ /** The contained [SimpleNames][SimpleName] */
+ val simpleNames: List
+
+ override val segments: List
+ get() = simpleNames.map { it.asString }
+
+ /**
+ * If the collection in [simpleNames] has more than one name, this value will be the last.
+ *
+ * example: Given a full name of `com.example.Outer.Inner`, with
+ * the [simpleNames] `[Outer, Inner]`, this value will be `Inner`.
+ */
+ val simplestName: SimpleName
+ get() = simpleNames.last()
+
+ companion object {
+
+ internal fun HasSimpleNames.checkSimpleNames() {
+ check(simpleNames.isNotEmpty()) {
+ "`simpleNames` must have at least one name, but this list is empty."
+ }
+ }
+ }
+}
+
+/**
+ * Convenience interface for providing a split list of name segments.
+ *
+ * ex: 'com.example.Subject' has the segments ['com', 'example', 'Subject']
+ */
+interface HasNameSegments {
+
+ /** ex: 'com.example.Subject' has the segments ['com', 'example', 'Subject'] */
+ val segments: List
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/TypeName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/TypeName.kt
new file mode 100644
index 0000000000..f6cc605c10
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/TypeName.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.PackageName.Companion.asPackageName
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import modulecheck.utils.lazy.unsafeLazy
+import modulecheck.utils.pluralString
+
+/** either a [ClassName] or a [TypeParameter] */
+sealed interface TypeName : Name, HasSimpleNames {
+ /** */
+ val nullable: Boolean
+
+ /** @return a new instance of [TypeName] with nullability set to true. */
+ fun makeNullable(): TypeName
+
+ /** @return a new instance of [TypeName] with nullability set to false. */
+ fun makeNotNullable(): TypeName
+}
+
+/**
+ * Represents a class name in the Kotlin language. It includes the
+ * package name, simple names, type arguments, and nullability.
+ *
+ * @property packageName The package name of the class.
+ * @property simpleNames The list of simple names of the class.
+ * @property typeArguments The list of type arguments of the class.
+ * @property nullable Indicates if the class name is nullable.
+ */
+data class ClassName(
+ override val packageName: PackageName,
+ override val simpleNames: List,
+ val typeArguments: List,
+ override val nullable: Boolean
+) : TypeName, NameWithPackageName {
+
+ /** ex: `com.example.MyGenericType` */
+ val asStringWithTypeParameters: String by unsafeLazy {
+ if (typeArguments.isEmpty()) {
+ asString
+ } else {
+ asString.plus(
+ typeArguments.joinToString(
+ separator = ", ",
+ prefix = "<",
+ postfix = ">"
+ ) { it.asString }
+ )
+ }
+ }
+
+ constructor(
+ packageName: String,
+ vararg simpleNames: String,
+ typeArguments: List = emptyList(),
+ nullable: Boolean = false
+ ) : this(
+ packageName = packageName.asPackageName(),
+ simpleNames = simpleNames.map { it.asSimpleName() },
+ typeArguments = typeArguments,
+ nullable = nullable
+ )
+
+ /** @return a new instance of [ClassName] with nullability set to true. */
+ override fun makeNullable(): TypeName = copy(nullable = true)
+
+ /** @return a new instance of [ClassName] with nullability set to false. */
+ override fun makeNotNullable(): TypeName = copy(nullable = false)
+
+ /**
+ * @param typeArguments The type arguments to parameterize the class name with.
+ * @return a new instance of [ClassName] with the provided type arguments.
+ */
+ fun parameterizedBy(vararg typeArguments: TypeName): ClassName =
+ copy(typeArguments = typeArguments.toList())
+}
+
+/**
+ * examples:
+ * ```
+ * T : CharSequence
+ * T
+ * /*...*/ T /*...*/ where T: Bar, T: Baz
+ * ```
+ *
+ * @property simpleName the simple name given to the generic, like `T` or `OutputT`
+ * @property bounds empty if it's a simple generic
+ * @property nullable `` vs ``
+ * @property variance The variance of the type parameter, can be either `IN`, `OUT`, or `null`.
+ */
+data class TypeParameter(
+ override val simpleName: SimpleName,
+ val bounds: List,
+ override val nullable: Boolean,
+ val variance: Variance?
+) : TypeName {
+
+ override val segments: List get() = listOf(simpleName.asString)
+ override val simpleNames: List get() = listOf(simpleName)
+ override val asString: String by unsafeLazy {
+ bounds.pluralString(
+ empty = { simpleName.asString },
+ single = { "${simpleName.asString} : ${it.asString}" },
+ moreThanOne = { simpleName.asString }
+ )
+ }
+
+ constructor(
+ name: SimpleName,
+ vararg bounds: TypeName
+ ) : this(simpleName = name, bounds = bounds.toList(), nullable = false, variance = null)
+
+ /** @return a new instance of [TypeParameter] with nullability set to true. */
+ override fun makeNullable(): TypeName = copy(nullable = true)
+
+ /** @return a new instance of [TypeParameter] with nullability set to false. */
+ override fun makeNotNullable(): TypeName = copy(nullable = false)
+
+ /** Represents the variance of a type parameter in the Kotlin language. */
+ enum class Variance {
+ OUT,
+ IN
+ }
+}
diff --git a/modulecheck-name/api/src/main/kotlin/modulecheck/name/UnqualifiedAndroidResourceName.kt b/modulecheck-name/api/src/main/kotlin/modulecheck/name/UnqualifiedAndroidResourceName.kt
new file mode 100644
index 0000000000..9672c238c6
--- /dev/null
+++ b/modulecheck-name/api/src/main/kotlin/modulecheck/name/UnqualifiedAndroidResourceName.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import modulecheck.name.SimpleName.Companion.asString
+import modulecheck.utils.lazy.unsafeLazy
+import java.io.File
+import kotlin.io.path.name
+
+/**
+ * example: `string.app_name`
+ *
+ * @property prefix 'string' in `R.string.app_name`
+ * @property identifier 'app_name' in `R.string.app_name`
+ */
+class UnqualifiedAndroidResourceName private constructor(
+ override val prefix: SimpleName,
+ override val identifier: SimpleName
+) : AndroidResourceName {
+
+ override val simpleNames: List by unsafeLazy {
+ listOf("R".asSimpleName(), prefix, identifier)
+ }
+ override val segments: List by unsafeLazy { simpleNames.map { it.asString } }
+ override val asString: String by unsafeLazy { simpleNames.asString() }
+
+ /**
+ * @return the fully qualified name of a generated Android resource, like
+ * `com.example.R.string.app_name` from the combination of `com.example.R` and `string.app_name`
+ */
+ fun toAndroidResourceNameWithRName(
+ androidRDeclaration: AndroidRName
+ ): AndroidResourceNameWithRName {
+ return AndroidName.qualifiedAndroidResource(
+ sourceR = AndroidRName(androidRDeclaration.packageName),
+ sourceResource = this
+ )
+ }
+
+ companion object {
+
+ private val XML_REGEX = """"?@\+?(.*)/(.*)"?""".toRegex()
+
+ /** `anim.foo` */
+ fun anim(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("anim".asSimpleName(), identifier = identifier)
+
+ /** `animator.foo` */
+ fun animator(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("animator".asSimpleName(), identifier = identifier)
+
+ /** `array.foo` */
+ fun array(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("array".asSimpleName(), identifier = identifier)
+
+ /** `bool.foo` */
+ fun bool(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("bool".asSimpleName(), identifier = identifier)
+
+ /** `color.foo` */
+ fun color(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("color".asSimpleName(), identifier = identifier)
+
+ /** `dimen.foo` */
+ fun dimen(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("dimen".asSimpleName(), identifier = identifier)
+
+ /** `drawable.foo` */
+ fun drawable(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("drawable".asSimpleName(), identifier = identifier)
+
+ /** `font.foo` */
+ fun font(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("font".asSimpleName(), identifier = identifier)
+
+ /** `id.foo` */
+ fun id(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("id".asSimpleName(), identifier = identifier)
+
+ /** `integer.foo` */
+ fun integer(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("integer".asSimpleName(), identifier = identifier)
+
+ /** `layout.foo` */
+ fun layout(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("layout".asSimpleName(), identifier = identifier)
+
+ /** `menu.foo` */
+ fun menu(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("menu".asSimpleName(), identifier = identifier)
+
+ /** `mipmap.foo` */
+ fun mipmap(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("mipmap".asSimpleName(), identifier = identifier)
+
+ /** `raw.foo` */
+ fun raw(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("raw".asSimpleName(), identifier = identifier)
+
+ /** `string.foo` */
+ fun string(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("string".asSimpleName(), identifier = identifier)
+
+ /** `style.foo` */
+ fun style(identifier: SimpleName): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName("style".asSimpleName(), identifier = identifier)
+
+ /** @return all resources declared within the given [file] */
+ fun fromFile(file: File): UnqualifiedAndroidResourceName? {
+ val dir = file.toPath().parent?.name ?: return null
+ val name = file.nameWithoutExtension
+
+ return when {
+ dir.startsWith("anim") -> anim(name.asSimpleName())
+ dir.startsWith("animator") -> animator(name.asSimpleName())
+ dir.startsWith("color") -> color(name.asSimpleName())
+ dir.startsWith("dimen") -> dimen(name.asSimpleName())
+ dir.startsWith("drawable") -> drawable(name.asSimpleName())
+ dir.startsWith("font") -> font(name.asSimpleName())
+ dir.startsWith("layout") -> layout(name.asSimpleName())
+ dir.startsWith("menu") -> menu(name.asSimpleName())
+ dir.startsWith("mipmap") -> mipmap(name.asSimpleName())
+ dir.startsWith("raw") -> raw(name.asSimpleName())
+ else -> null
+ }
+ }
+
+ /** @return `id.foo` for [type] `id` and [name] `foo` */
+ fun fromValuePair(type: String, name: String): UnqualifiedAndroidResourceName? {
+ val fixedName = name.replace('.', '_')
+ return when (type.removePrefix("android:")) {
+ "anim" -> anim(fixedName.asSimpleName())
+ "animator" -> animator(fixedName.asSimpleName())
+ "array" -> array(fixedName.asSimpleName())
+ "bool" -> bool(fixedName.asSimpleName())
+ "color" -> color(fixedName.asSimpleName())
+ "dimen" -> dimen(fixedName.asSimpleName())
+ "drawable" -> drawable(fixedName.asSimpleName())
+ "font" -> font(fixedName.asSimpleName())
+ "id" -> id(fixedName.asSimpleName())
+ "integer" -> integer(fixedName.asSimpleName())
+ "integer-array" -> array(fixedName.asSimpleName())
+ "layout" -> layout(fixedName.asSimpleName())
+ "menu" -> menu(fixedName.asSimpleName())
+ "mipmap" -> mipmap(fixedName.asSimpleName())
+ "raw" -> raw(fixedName.asSimpleName())
+ "string" -> string(fixedName.asSimpleName())
+ "style" -> style(fixedName.asSimpleName())
+ else -> null
+ }
+ }
+
+ /** @return a resource declaration from a string in XML, like `@+id/______` */
+ fun fromXmlString(str: String): UnqualifiedAndroidResourceName? {
+ val (prefix, name) = XML_REGEX.find(str)?.destructured ?: return null
+
+ return fromValuePair(prefix, name)
+ }
+ }
+}
diff --git a/modulecheck-name/api/src/test/kotlin/modulecheck/name/AsNameWithPackageNameTest.kt b/modulecheck-name/api/src/test/kotlin/modulecheck/name/AsNameWithPackageNameTest.kt
new file mode 100644
index 0000000000..50286c4fc5
--- /dev/null
+++ b/modulecheck-name/api/src/test/kotlin/modulecheck/name/AsNameWithPackageNameTest.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import io.kotest.matchers.shouldBe
+import modulecheck.name.PackageName.Companion.asPackageName
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import org.jetbrains.kotlin.name.FqName
+import org.junit.jupiter.api.Nested
+import org.junit.jupiter.api.Test
+
+internal class AsNameWithPackageNameTest {
+
+ @Nested
+ inner class `FqName` {
+
+ @Test
+ fun `FqName asDeclaredName with nested type treats outer type as simple name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Outer".asSimpleName(), "Inner".asSimpleName())
+
+ val asString = packageName.appendAsString(simpleNames.map { it.asString })
+
+ FqName(asString).asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with no language creates agnostic declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ val asString = packageName.appendAsString(simpleNames.map { it.asString })
+
+ FqName(asString).asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with Kotlin language creates Kotlin declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ val asString = packageName.appendAsString(simpleNames.map { it.asString })
+
+ FqName(asString).asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with Java language creates Java declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ val asString = packageName.appendAsString(simpleNames.map { it.asString })
+
+ FqName(asString).asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with Java and Kotlin languages creates agnostic declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ val asString = packageName.appendAsString(simpleNames.map { it.asString })
+
+ FqName(asString).asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+ }
+
+ @Nested
+ inner class `iterable receiver` {
+
+ @Test
+ fun `asNameWithPackageName with no language creates agnostic declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ simpleNames.asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with Kotlin language creates Kotlin declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ simpleNames.asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with Java language creates Java declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ simpleNames.asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+
+ @Test
+ fun `asNameWithPackageName with Java and Kotlin languages creates agnostic declared name`() {
+
+ val packageName = "com.test".asPackageName()
+ val simpleNames = listOf("Subject".asSimpleName())
+
+ simpleNames.asNameWithPackageName(packageName) shouldBe NameWithPackageNameImpl(
+ packageName = packageName,
+ simpleNames = simpleNames
+ )
+ }
+ }
+}
diff --git a/modulecheck-name/api/src/test/kotlin/modulecheck/name/NameTest.kt b/modulecheck-name/api/src/test/kotlin/modulecheck/name/NameTest.kt
new file mode 100644
index 0000000000..2d6417f684
--- /dev/null
+++ b/modulecheck-name/api/src/test/kotlin/modulecheck/name/NameTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import modulecheck.testing.BaseTest
+import modulecheck.testing.TestEnvironment
+import org.junit.jupiter.api.Test
+
+class NameTest : BaseTest() {
+
+ @Test
+ fun `sorting should be by name first, then the name of the Name class`() {
+ val packageNames = listOf("a", "b", "c", "d")
+ val simpleNames = listOf("X", "Y", "Z")
+
+ val instances = packageNames
+ .reversed()
+ .flatMap { packageName ->
+ simpleNames
+ .reversed()
+ .flatMap { simpleName ->
+
+ listOf(
+ NameWithPackageName(
+ PackageName(packageName),
+ listOf(simpleName.asSimpleName())
+ ),
+ NameWithPackageName(
+ PackageName(packageName),
+ listOf(simpleName.asSimpleName())
+ ),
+ NameWithPackageName(
+ PackageName(packageName),
+ listOf(simpleName.asSimpleName())
+ ),
+ AndroidRName(PackageName(packageName))
+ )
+ }
+ }
+ .shuffled()
+ // Android R names will be duplicated, so clean those up
+ .distinctBy { it.asString to it::class }
+
+ val prettySorted = instances.sorted()
+ .joinToString("\n") { "${it::class.java.simpleName.padStart(28)} ${it.asString}" }
+
+ prettySorted shouldBe """
+ AndroidRName a.R
+ NameWithPackageNameImpl a.X
+ NameWithPackageNameImpl a.Y
+ NameWithPackageNameImpl a.Z
+ AndroidRName b.R
+ NameWithPackageNameImpl b.X
+ NameWithPackageNameImpl b.Y
+ NameWithPackageNameImpl b.Z
+ AndroidRName c.R
+ NameWithPackageNameImpl c.X
+ NameWithPackageNameImpl c.Y
+ NameWithPackageNameImpl c.Z
+ AndroidRName d.R
+ NameWithPackageNameImpl d.X
+ NameWithPackageNameImpl d.Y
+ NameWithPackageNameImpl d.Z
+ """.trimIndent()
+ }
+}
diff --git a/modulecheck-name/api/src/test/kotlin/modulecheck/name/PackageNameTest.kt b/modulecheck-name/api/src/test/kotlin/modulecheck/name/PackageNameTest.kt
new file mode 100644
index 0000000000..6cca014454
--- /dev/null
+++ b/modulecheck-name/api/src/test/kotlin/modulecheck/name/PackageNameTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import io.kotest.assertions.throwables.shouldNotThrow
+import io.kotest.matchers.shouldBe
+import io.kotest.property.Arb
+import io.kotest.property.arbitrary.stringPattern
+import modulecheck.testing.asTests
+import modulecheck.utils.interpuncts
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestFactory
+
+internal class PackageNameTest {
+
+ @TestFactory
+ fun `any non-empty name string just becomes wrapped by the class`() =
+ Arb.stringPattern("""(.|\s)*\S(.|\s)*""")
+ .asTests { name ->
+
+ shouldNotThrow {
+ PackageName(name).asString shouldBe name
+ }
+ }
+
+ @Test
+ fun `an empty package name becomes DEFAULT`() {
+
+ PackageName("") shouldBe PackageName.DEFAULT
+ }
+
+ @Test
+ fun `a null package name becomes DEFAULT`() {
+
+ PackageName(null) shouldBe PackageName.DEFAULT
+ }
+
+ @TestFactory
+ fun `a blank package name becomes DEFAULT`() = Arb.stringPattern("""[^\S\r\n]+""")
+ .asTests(testName = { it.interpuncts }) { name ->
+
+ PackageName(name) shouldBe PackageName.DEFAULT
+ }
+}
diff --git a/modulecheck-name/api/src/test/kotlin/modulecheck/name/SimpleNameTest.kt b/modulecheck-name/api/src/test/kotlin/modulecheck/name/SimpleNameTest.kt
new file mode 100644
index 0000000000..0d484af1ca
--- /dev/null
+++ b/modulecheck-name/api/src/test/kotlin/modulecheck/name/SimpleNameTest.kt
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.name
+
+import io.kotest.assertions.throwables.shouldNotThrow
+import io.kotest.assertions.throwables.shouldThrowWithMessage
+import io.kotest.matchers.shouldBe
+import io.kotest.property.Arb
+import io.kotest.property.arbitrary.stringPattern
+import modulecheck.testing.asTests
+import modulecheck.testing.forAllBlocking
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestFactory
+
+internal class SimpleNameTest {
+
+ @TestFactory
+ fun `valid java identifiers without backticks are allowed`() = listOf(
+ "_test", "test", "t", "T", "\$var", "_", "$", "_123", "$123", "test123", "test_var", "TestVar",
+ "TEST_VAR", "test\$var", "\$test_var", "_\$test", "T123", "_T123", "\$T123", "test$123",
+ "Test$123", "TEST$123", "test_var123", "Test_var123", "TEST_VAR123", "_\$test123", "_Test123",
+ "_TEST123", "\$test123", "\$Test123", "\$TEST123", "test\$var123", "Test\$var123",
+ "TEST\$var123", "_1", "$1", "test1", "Test1", "TEST1", "test_var1", "Test_var1", "TEST_VAR1",
+ "test\$var1", "Test\$var1", "TEST\$var1", "_test1", "_Test1", "_TEST1", "\$test1", "\$Test1",
+ "validName", "_validName", "\$validName", "VALIDNAME", "Valid_Name", "_1234", "$1234",
+ "name1234", "name_1234", "name$1234", "n", "_n", "\$n", "N", "n_1", "_a", "\$a", "a1", "a_1",
+ "a$1", "b", "_b", "\$b", "B", "b_1", "_c", "\$c", "c1", "c_1", "c$1", "d", "_d", "\$d", "D",
+ "d_1", "_e", "\$e", "e1", "e_1", "e$1", "f", "_f", "\$f", "F", "f_1", "_g", "\$g", "g1", "g_1",
+ "g$1"
+ )
+ .asTests { name ->
+
+ shouldNotThrow {
+ SimpleName(name).asString shouldBe name
+ }
+ }
+
+ @TestFactory
+ fun `invalid java identifiers without backticks are not allowed`() = listOf(
+ "1invalid", "#invalid", "@invalid", "!invalid", " invalid",
+ "-invalid", "&invalid", "*invalid", "(invalid", ")invalid",
+ "[invalid", "{invalid", "]invalid", "}invalid", ":invalid",
+ ";invalid", ",invalid", "invalid",
+ "/invalid", "?invalid", "~invalid", "`invalid", "'invalid",
+ "\"invalid", "|invalid", "\\invalid", "+invalid", "=invalid",
+ "invalid ", "invalid\n", "invalid\t", "invalid\b", "invalid\\f",
+ "invalid\r", "invalid\\v", "invalid\\x1B", "invalid\u0085", "invalid\u2028",
+ "invalid\u2029", "invalid\uFEFF", "invalid\uFFF9", "invalid\uFFFA", "invalid\uFFFB",
+ "invalid\uFFFC", "invalid\uFFFD", "invalid\uFFFE", "invalid\uFFFF", "invalid\u10000",
+ "invalid\u10FFFF", "invalid\u1FFFFF", "invalid\u3FFFFFF", "invalid\u7FFFFFFF",
+ "invalid\uFFFFFFFF"
+ )
+ .asTests { name ->
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @TestFactory
+ fun `wrapped in backticks and allowed`() = listOf(
+ "`validName`", "`_validName`", "`\$validName`", "`VALIDNAME`", "`Valid_Name`", "`_1234`",
+ "`$1234`", "`name1234`", "`name_1234`", "`name$1234`", "`n`", "`_n`", "`\$n`", "`N`", "`n_1`",
+ "`_a`", "`\$a`", "`a1`", "`a_1`", "`a$1`", "`b`", "`_b`", "`\$b`", "`B`", "`b_1`", "`_c`",
+ "`\$c`", "`c1`", "`c_1`", "`c$1`", "`d`", "`_d`", "`\$d`", "`D`", "`d_1`", "`_e`", "`\$e`",
+ "`e1`", "`e_1`", "`e$1`", "`f`", "`_f`", "`valid name`", "` validName`", "`validName `",
+ "` valid Name `", "`valid Name`", "`Name with spaces`", "` multiple spaces `",
+ "` leading space`", "`trailing space `", "` space around $ `", "` space around _ `",
+ "` space before 1`", "`1 after space`", "` spaces around digits 123 `", "`single space`",
+ "` multiple spaces `", "` space before and after `", "` space around keyword if `",
+ "` space around keyword while `", "` space around keyword for `",
+ "` space around keyword when `", "` space around keyword else `",
+ "` space around keyword try `", "` space around keyword catch `",
+ "` space around keyword finally `", "` space around keyword do `",
+ "` space around keyword return `", "` space around keyword continue `",
+ "` space around keyword break `", "` space around keyword class `",
+ "` space around keyword object `", "` space around keyword interface `",
+ "` space around keyword enum `", "` space around keyword annotation `",
+ "` space around keyword typealias `", "` space around keyword constructor `",
+ "` space around keyword by `", "` space around keyword get `", "` space around keyword set `",
+ "` space around keyword import `", "` space around keyword as `", "` space around keyword is `",
+ "` space around keyword in `", "` space around keyword out `",
+ "` space around keyword override `", "` space around keyword public `",
+ "` space around keyword private `", "` space around keyword internal `",
+ "` space around keyword protected `"
+ )
+ .asTests { name ->
+
+ shouldNotThrow {
+ SimpleName(name).asString shouldBe name
+ }
+ }
+
+ @TestFactory
+ fun `wrapped in backticks but not allowed`() = listOf(
+ "``",
+ "`\n`",
+ "`\nnewline`",
+ "`newline\n`"
+ )
+ .asTests { name ->
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @TestFactory
+ fun `any non-empty name in backticks without newlines or another backtick is allowed`() =
+ Arb.stringPattern("""`[^\n\`]+`""")
+ .asTests { name ->
+
+ shouldNotThrow {
+ SimpleName(name).asString shouldBe name
+ }
+ }
+
+ @Test
+ fun `an empty string in backticks is not allowed`() {
+
+ val name = "``"
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @Test
+ fun `a name with whitespaces is allowed if wrapped in backticks`() {
+
+ val name = "`a name with whitespaces is allowed if wrapped in backticks`"
+
+ shouldNotThrow {
+ SimpleName(name).asString shouldBe name
+ }
+ }
+
+ @TestFactory
+ fun `a name without backticks with a white space is not allowed`() = Arb.stringPattern("""\s+""")
+ .forAllBlocking { name ->
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @TestFactory
+ fun `a name with a dot is not allowed`() = Arb.stringPattern("""\.+""")
+ .forAllBlocking { name ->
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @Test
+ fun `an empty name is not allowed`() {
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: ``"
+ ) {
+ SimpleName("")
+ }
+ }
+
+ @TestFactory
+ fun `a blank name is not allowed`() = Arb.stringPattern("\\s*")
+ .asTests(
+ testName = { "`$it`" }
+ ) { name ->
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @Test
+ fun `an empty package name becomes DEFAULT`() {
+
+ PackageName("") shouldBe PackageName.DEFAULT
+ }
+
+ @Test
+ fun `a blank package name becomes DEFAULT`() {
+
+ Arb.stringPattern("\\s*")
+ .forAllBlocking { name ->
+
+ PackageName(name) shouldBe PackageName.DEFAULT
+ }
+ }
+
+ @Test
+ fun `a backticked name with valid characters and spaces is allowed`() {
+ val name = "`a valid name with spaces`"
+
+ shouldNotThrow {
+ SimpleName(name).asString shouldBe name
+ }
+ }
+
+ @Test
+ fun `a backticked name with a backtick inside it throws an exception`() {
+ val name = "`a name ` with a backtick`"
+
+ shouldThrowWithMessage(
+ "SimpleName names must be valid Java identifier " +
+ "without a dot qualifier. This name was: `$name`"
+ ) {
+ SimpleName(name)
+ }
+ }
+
+ @Test
+ fun `a backticked name with only spaces is allowed`() {
+ val name = "` `"
+
+ shouldNotThrow {
+ SimpleName(name).asString shouldBe name
+ }
+ }
+}
diff --git a/modulecheck-name/testing/api/testing.api b/modulecheck-name/testing/api/testing.api
new file mode 100644
index 0000000000..3600c9ecd3
--- /dev/null
+++ b/modulecheck-name/testing/api/testing.api
@@ -0,0 +1,59 @@
+public abstract interface class NameTest : modulecheck/testing/assert/TrimmedAsserts {
+ public fun agnostic-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static synthetic fun agnostic-9PpwmVA$default (LNameTest;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/NameWithPackageName;
+ public fun androidR-p04zEWQ (Ljava/lang/String;)Lmodulecheck/name/AndroidRName;
+ public static synthetic fun androidR-p04zEWQ$default (LNameTest;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/AndroidRName;
+ public fun java-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static synthetic fun java-9PpwmVA$default (LNameTest;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/NameWithPackageName;
+ public fun kotlin-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static synthetic fun kotlin-9PpwmVA$default (LNameTest;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/NameWithPackageName;
+ public fun shouldBe (Ljava/util/Collection;Ljava/util/Collection;)V
+ public fun shouldBe (Ljava/util/List;Ljava/util/Collection;)V
+ public fun shouldBe (Lmodulecheck/utils/lazy/LazyDeferred;Ljava/util/Collection;)V
+ public fun shouldBe (Lmodulecheck/utils/lazy/LazySet;Ljava/util/Collection;)V
+ public fun shouldBeJvmFile (Lmodulecheck/parsing/source/JvmFile;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class NameTest$JvmFileBuilder {
+ public fun ()V
+ public final fun apiReferences (Lkotlin/jvm/functions/Function1;)V
+ public final fun declarations (Lkotlin/jvm/functions/Function1;)V
+ public final fun getApiNames ()Ljava/util/List;
+ public final fun getDeclarations ()Ljava/util/List;
+ public final fun getNames ()Ljava/util/List;
+ public final fun references (Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class NameTest$JvmFileBuilder$ApiReferenceBuilder : NameTest$JvmFileBuilder$ReferenceBuilder {
+ public fun (LNameTest$JvmFileBuilder;)V
+}
+
+public final class NameTest$JvmFileBuilder$DeclarationsBuilder {
+ public fun (LNameTest$JvmFileBuilder;)V
+ public final fun agnostic-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static synthetic fun agnostic-9PpwmVA$default (LNameTest$JvmFileBuilder$DeclarationsBuilder;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/NameWithPackageName;
+ public final fun java-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static synthetic fun java-9PpwmVA$default (LNameTest$JvmFileBuilder$DeclarationsBuilder;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/NameWithPackageName;
+ public final fun kotlin-9PpwmVA (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/NameWithPackageName;
+ public static synthetic fun kotlin-9PpwmVA$default (LNameTest$JvmFileBuilder$DeclarationsBuilder;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/NameWithPackageName;
+}
+
+public final class NameTest$JvmFileBuilder$NormalReferenceBuilder : NameTest$JvmFileBuilder$ReferenceBuilder {
+ public fun (LNameTest$JvmFileBuilder;)V
+}
+
+public class NameTest$JvmFileBuilder$ReferenceBuilder {
+ public fun (Ljava/util/List;)V
+ public final fun androidDataBinding (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/AndroidDataBindingName;
+ public final fun androidR-p04zEWQ (Ljava/lang/String;)Lmodulecheck/name/AndroidRName;
+ public static synthetic fun androidR-p04zEWQ$default (LNameTest$JvmFileBuilder$ReferenceBuilder;Ljava/lang/String;ILjava/lang/Object;)Lmodulecheck/name/AndroidRName;
+ public final fun java (Ljava/lang/String;)Lmodulecheck/name/Name;
+ public final fun kotlin (Ljava/lang/String;)Lmodulecheck/name/Name;
+ public final fun qualifiedAndroidResource (Ljava/lang/String;Ljava/lang/String;)Lmodulecheck/name/AndroidResourceNameWithRName;
+ public final fun unqualifiedAndroidResource (Ljava/lang/String;)Lmodulecheck/name/UnqualifiedAndroidResourceName;
+}
+
+public final class NameTestKt {
+ public static final fun prettyPrint (Ljava/util/Collection;)Ljava/lang/String;
+}
+
diff --git a/modulecheck-name/testing/build.gradle.kts b/modulecheck-name/testing/build.gradle.kts
new file mode 100644
index 0000000000..965e433530
--- /dev/null
+++ b/modulecheck-name/testing/build.gradle.kts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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.
+ */
+
+plugins {
+ id("mcbuild")
+}
+
+mcbuild {
+ published(
+ artifactId = "modulecheck-name-testing"
+ )
+}
+
+dependencies {
+
+ api(project(path = ":modulecheck-internal-testing"))
+ api(project(path = ":modulecheck-name:api"))
+ api(project(path = ":modulecheck-parsing:source:api"))
+ api(project(path = ":modulecheck-utils:lazy"))
+
+ implementation(libs.bundles.junit)
+ implementation(libs.bundles.kotest)
+ implementation(libs.kotlin.reflect)
+
+ implementation(project(path = ":modulecheck-utils:trace"))
+}
diff --git a/modulecheck-name/testing/src/main/kotlin/modulecheck/name/testing/NameTest.kt b/modulecheck-name/testing/src/main/kotlin/modulecheck/name/testing/NameTest.kt
new file mode 100644
index 0000000000..02e5dc2c62
--- /dev/null
+++ b/modulecheck-name/testing/src/main/kotlin/modulecheck/name/testing/NameTest.kt
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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.
+ */
+
+import io.kotest.assertions.asClue
+import io.kotest.assertions.assertSoftly
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.runBlocking
+import modulecheck.name.AndroidDataBindingName
+import modulecheck.name.AndroidRName
+import modulecheck.name.AndroidResourceNameWithRName
+import modulecheck.name.Name
+import modulecheck.name.NameWithPackageName
+import modulecheck.name.PackageName
+import modulecheck.name.PackageName.Companion.asPackageName
+import modulecheck.name.SimpleName
+import modulecheck.name.SimpleName.Companion.asSimpleName
+import modulecheck.name.SimpleName.Companion.stripPackageNameFromFqName
+import modulecheck.name.TypeParameter
+import modulecheck.name.UnqualifiedAndroidResourceName
+import modulecheck.name.asNameWithPackageName
+import modulecheck.parsing.source.JvmFile
+import modulecheck.testing.assert.TrimmedAsserts
+import modulecheck.testing.assert.requireNotNullOrFail
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.LazySet
+import modulecheck.utils.trace.Trace
+
+/**
+ * Interface for testing [Name] instances. It provides a set of helper
+ * methods and builders for creating and comparing [Name] instances.
+ */
+interface NameTest : TrimmedAsserts {
+
+ /** Builder for creating a [JvmFile] instance for testing. */
+ class JvmFileBuilder {
+
+ /** all names in the file */
+ val names: MutableList = mutableListOf()
+
+ /** all names in the file which are part of its public API */
+ val apiNames: MutableList = mutableListOf()
+
+ /** all named declarations in the file */
+ val declarations: MutableList = mutableListOf()
+
+ /**
+ * Adds normal references to the [JvmFile] being built.
+ *
+ * @param builder The builder function for adding references.
+ */
+ fun references(builder: NormalReferenceBuilder.() -> Unit) {
+ NormalReferenceBuilder().builder()
+ }
+
+ /**
+ * Adds API references to the [JvmFile] being built.
+ *
+ * @param builder The builder function for adding API references.
+ */
+ fun apiReferences(builder: ApiReferenceBuilder.() -> Unit) {
+ ApiReferenceBuilder().builder()
+ }
+
+ /**
+ * Adds declarations to the [JvmFile] being built.
+ *
+ * @param builder The builder function for adding declarations.
+ */
+ fun declarations(builder: DeclarationsBuilder.() -> Unit) {
+ DeclarationsBuilder().builder()
+ }
+
+ /**
+ * Base class for building references.
+ *
+ * @property target The target list to add the references to.
+ */
+ open class ReferenceBuilder(
+ private val target: MutableList
+ ) {
+
+ /**
+ * Adds an [AndroidRName] to the target list.
+ *
+ * @param packageName The package name of the Android R class.
+ * @return The created [AndroidRName].
+ */
+ fun androidR(packageName: PackageName = PackageName("com.test")): AndroidRName =
+ AndroidRName(packageName)
+ .also { target.add(it) }
+
+ /**
+ * Adds an [AndroidDataBindingName] to the target list.
+ *
+ * @param packageName ex: `com.example.library`
+ * @param layoutSimpleName ex: `activity_main`
+ * @return The created [AndroidDataBindingName].
+ */
+ fun androidDataBinding(
+ packageName: String,
+ layoutSimpleName: String
+ ): AndroidDataBindingName = AndroidDataBindingName(
+ packageName = packageName.asPackageName(),
+ sourceLayout = UnqualifiedAndroidResourceName.layout(layoutSimpleName.asSimpleName())
+ )
+ .apply { target.add(this) }
+
+ /**
+ * Adds a qualified [AndroidResourceNameWithRName] to the target list.
+ *
+ * @param packageName ex: `com.example.library`
+ * @param resourceName ex: `layout.activity_main`
+ * @return The created [AndroidResourceNameWithRName].
+ */
+ fun qualifiedAndroidResource(
+ packageName: String,
+ resourceName: String
+ ): AndroidResourceNameWithRName {
+ val (type, simple) = resourceName.split('.')
+ return AndroidResourceNameWithRName(
+ androidRName = AndroidRName(packageName.asPackageName()),
+ resourceName = UnqualifiedAndroidResourceName.fromValuePair(type, simple)
+ .requireNotNullOrFail { "no resource is declared for $resourceName" }
+ )
+ .apply { target.add(this) }
+ }
+
+ /**
+ * Adds an unqualified [UnqualifiedAndroidResourceName] to the target list.
+ *
+ * @param name The name of the Android resource.
+ * @return The created [UnqualifiedAndroidResourceName].
+ */
+ fun unqualifiedAndroidResource(name: String): UnqualifiedAndroidResourceName =
+ UnqualifiedAndroidResourceName.layout(name.asSimpleName())
+ .also { target.add(it) }
+
+ /**
+ * Adds a Kotlin [Name] to the target list.
+ *
+ * @param name The name of the Kotlin class.
+ * @return The created [Name].
+ */
+ fun kotlin(name: String): Name = name.asSimpleName().also { target.add(it) }
+
+ /**
+ * Adds a Java [Name] to the target list.
+ *
+ * @param name The name of the Java class.
+ * @return The created [Name].
+ */
+ fun java(name: String): Name = name.asSimpleName().also { target.add(it) }
+ }
+
+ /** Builder for normal references. */
+ inner class NormalReferenceBuilder : ReferenceBuilder(names)
+
+ /** Builder for API references. */
+ inner class ApiReferenceBuilder : ReferenceBuilder(apiNames)
+
+ /** Builder for declarations. */
+ inner class DeclarationsBuilder {
+ /**
+ * Adds a Kotlin [NameWithPackageName] to the declarations list.
+ *
+ * @param name The name of the Kotlin class.
+ * @param packageName The package name of the Kotlin class.
+ * @return The created [NameWithPackageName].
+ */
+ fun kotlin(
+ name: String,
+ packageName: PackageName = PackageName("com.subject")
+ ): NameWithPackageName = NameWithPackageName(
+ packageName,
+ name.stripPackageNameFromFqName(packageName)
+ )
+ .also { declarations.add(it) }
+
+ /**
+ * Adds a Java [NameWithPackageName] to the declarations list.
+ *
+ * @param name The name of the Java class.
+ * @param packageName The package name of the Java class.
+ * @return The created [NameWithPackageName].
+ */
+ fun java(
+ name: String,
+ packageName: PackageName = PackageName("com.subject")
+ ): NameWithPackageName = NameWithPackageName(
+ packageName,
+ name.stripPackageNameFromFqName(packageName)
+ )
+ .also { declarations.add(it) }
+
+ /**
+ * Adds an agnostic [NameWithPackageName] to the declarations list.
+ *
+ * @param name The name of the class.
+ * @param packageName The package name of the class.
+ * @return The created [NameWithPackageName].
+ */
+ fun agnostic(
+ name: String,
+ packageName: PackageName = PackageName("com.subject")
+ ): NameWithPackageName = name.stripPackageNameFromFqName(packageName)
+ .asNameWithPackageName(packageName)
+ .also { declarations.add(it) }
+ }
+ }
+
+ /**
+ * Asserts that the receiver [JvmFile] is equal to
+ * the [JvmFile] built by the provided configuration.
+ *
+ * @param config The configuration for building the [JvmFile] to compare with.
+ */
+ infix fun JvmFile.shouldBeJvmFile(config: JvmFileBuilder.() -> Unit) {
+
+ val other = JvmFileBuilder().apply(config)
+
+ assertSoftly {
+ "references".asClue {
+ references shouldBe other.names
+ }
+ "api references".asClue {
+ apiReferences shouldBe other.apiNames
+ }
+ "declarations".asClue {
+ declarations shouldBe other.declarations
+ }
+ }
+ }
+
+ /**
+ * Asserts that the receiver collection of [NameWithPackageName] is equal to the other collection.
+ *
+ * @param other The other collection of [NameWithPackageName] to compare with.
+ */
+ infix fun Collection.shouldBe(other: Collection) {
+ prettyPrint().trimmedShouldBe(other.prettyPrint(), NameTest::class)
+ }
+
+ /**
+ * Asserts that the receiver [LazySet] of [Name] is equal to the other collection.
+ *
+ * @param other The other collection of [Name] to compare with.
+ */
+ infix fun LazySet.shouldBe(other: Collection) {
+ runBlocking(Trace.start(NameTest::class)) {
+ toList()
+ .distinct()
+ .prettyPrint().trimmedShouldBe(other.prettyPrint(), NameTest::class)
+ }
+ }
+
+ /**
+ * Asserts that the receiver [LazyDeferred] of [Set] of [Name] is equal to the other collection.
+ *
+ * @param other The other collection of [Name] to compare with.
+ */
+ infix fun LazyDeferred>.shouldBe(other: Collection) {
+ runBlocking(Trace.start(NameTest::class)) {
+ await()
+ .distinct()
+ .prettyPrint().trimmedShouldBe(other.prettyPrint(), NameTest::class)
+ }
+ }
+
+ /**
+ * Asserts that the receiver list of [LazySet.DataSource]
+ * of [Name] is equal to the other collection.
+ *
+ * @param other The other collection of [Name] to compare with.
+ */
+ infix fun List>.shouldBe(other: Collection) {
+ runBlocking(Trace.start(NameTest::class)) {
+ flatMap { it.get() }
+ .distinct()
+ .prettyPrint()
+ .trimmedShouldBe(other.prettyPrint(), NameTest::class)
+ }
+ }
+
+ /**
+ * Creates a Kotlin [NameWithPackageName] with the provided name and package name.
+ *
+ * @param name The name of the Kotlin class.
+ * @param packageName The package name of the Kotlin class.
+ * @return The created [NameWithPackageName].
+ */
+ fun kotlin(
+ name: String,
+ packageName: PackageName = PackageName("com.subject")
+ ): NameWithPackageName =
+ name.stripPackageNameFromFqName(packageName).asNameWithPackageName(packageName)
+
+ /**
+ * Creates a Java [NameWithPackageName] with the provided name and package name.
+ *
+ * @param name The name of the Java class.
+ * @param packageName The package name of the Java class.
+ * @return The created [NameWithPackageName].
+ */
+ fun java(
+ name: String,
+ packageName: PackageName = PackageName("com.subject")
+ ): NameWithPackageName =
+ name.stripPackageNameFromFqName(packageName).asNameWithPackageName(packageName)
+
+ /**
+ * Creates an agnostic [NameWithPackageName] with the provided name and package name.
+ *
+ * @param name The name of the class.
+ * @param packageName The package name of the```kotlin
+ * class. @return The created [NameWithPackageName].
+ */
+ fun agnostic(
+ name: String,
+ packageName: PackageName = PackageName("com.subject")
+ ): NameWithPackageName =
+ name.stripPackageNameFromFqName(packageName).asNameWithPackageName(packageName)
+
+ /**
+ * Creates an [AndroidRName] with the provided package name.
+ *
+ * @param packageName The package name of the Android R class.
+ * @return The created [AndroidRName].
+ */
+ fun androidR(packageName: PackageName = PackageName("com.test")): AndroidRName =
+ AndroidRName(packageName)
+}
+
+/**
+ * Pretty prints a collection of [Name] instances.
+ *
+ * @return The pretty printed string.
+ */
+fun Collection.prettyPrint(): String = asSequence()
+ .map { name ->
+ val typeName = when (name) {
+ // references
+ is UnqualifiedAndroidResourceName -> "unqualifiedAndroidResource"
+ is AndroidRName -> "androidR"
+ is AndroidResourceNameWithRName -> "qualifiedAndroidResource"
+ is AndroidDataBindingName -> "androidDataBinding"
+ is TypeParameter -> "typeParameter"
+ // is Name -> when {
+ // Name.isJava() -> "java"
+ // Name.isKotlin() -> "kotlin"
+ // Name.isXml() -> "xml"
+ // else -> throw IllegalArgumentException("???")
+ // }
+
+ // is AndroidRDeclaredName -> "androidR"
+ // is UnqualifiedAndroidResource -> Name.prefix.name
+ // is QualifiedAndroidResourceDeclaredName -> "qualifiedAndroidResource"
+ // is AndroidDataBindingDeclaredName -> "androidDataBinding"
+
+ // declarations
+ is NameWithPackageName -> {
+ when {
+ // Name.languages.containsAll(setOf(KOTLIN, JAVA)) -> "agnostic"
+ // Name.languages.contains(KOTLIN) -> "kotlin"
+ // Name.languages.contains(JAVA) -> "java"
+ // Name.languages.contains(XML) -> "xml"
+ else -> "throw IllegalArgumentException(???)"
+ }
+ }
+ // package
+ is PackageName -> "packageName"
+ is SimpleName -> "simpleName"
+ }
+ typeName to name
+ }
+ .groupBy { it.first }
+ .toList()
+ .sortedBy { it.first }
+ .joinToString("\n") { (typeName, pairs) ->
+
+ pairs.map { it.second }
+ .sortedBy { it.asString }
+ .joinToString("\n", "$typeName {\n", "\n}") { "\t${it.asString}" }
+ }
diff --git a/modulecheck-parsing/element/api/api/api.api b/modulecheck-parsing/element/api/api/api.api
new file mode 100644
index 0000000000..9377300d2a
--- /dev/null
+++ b/modulecheck-parsing/element/api/api/api.api
@@ -0,0 +1,358 @@
+public abstract interface class modulecheck/parsing/element/Declared : modulecheck/parsing/source/HasPackageName, modulecheck/parsing/source/HasSimpleNames {
+ public abstract fun getDeclaredName ()Lmodulecheck/parsing/source/DeclaredName;
+ public fun isApi ()Z
+}
+
+public abstract interface class modulecheck/parsing/element/HasJavaVisibility : modulecheck/parsing/element/HasVisibility {
+ public abstract fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McJavaVisibility;
+}
+
+public abstract interface class modulecheck/parsing/element/HasKtVisibility : modulecheck/parsing/element/HasVisibility {
+ public abstract fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McKtVisibility;
+}
+
+public abstract interface class modulecheck/parsing/element/HasVisibility {
+ public abstract fun getVisibility ()Lmodulecheck/parsing/element/McVisibility;
+}
+
+public abstract interface class modulecheck/parsing/element/McAnnotated {
+ public abstract fun getAnnotations ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McAnnotation : modulecheck/parsing/element/McElement, modulecheck/parsing/element/McElementWithParent {
+ public abstract fun getReferenceName ()Lmodulecheck/utils/lazy/LazyDeferred;
+}
+
+public abstract interface class modulecheck/parsing/element/McAnnotation$McKtAnnotation : modulecheck/parsing/element/McAnnotation, modulecheck/parsing/element/McElementWithParent, modulecheck/parsing/element/McKtElement {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McAnnotationArgument : modulecheck/parsing/element/McElement, modulecheck/parsing/element/McElementWithParent {
+ public abstract fun getType ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public abstract fun getValue ()Ljava/lang/Object;
+}
+
+public abstract interface class modulecheck/parsing/element/McAnnotationArgument$McKtAnnotationArgument : modulecheck/parsing/element/McAnnotationArgument, modulecheck/parsing/element/McElementWithParent, modulecheck/parsing/element/McKtElement {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McCallable : modulecheck/parsing/element/HasVisibility, modulecheck/parsing/element/McAnnotated, modulecheck/parsing/element/McElement, modulecheck/parsing/element/McElementWithParent {
+}
+
+public abstract interface class modulecheck/parsing/element/McCallable$McJavaCallable : modulecheck/parsing/element/McCallable, modulecheck/parsing/element/McJavaElement {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McJavaElement;
+ public abstract fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McJavaVisibility;
+}
+
+public abstract interface class modulecheck/parsing/element/McCallable$McKtCallable : modulecheck/parsing/element/McCallable, modulecheck/parsing/element/McKtElement {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtCallableDeclaration;
+ public abstract fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McKtVisibility;
+}
+
+public abstract interface class modulecheck/parsing/element/McElement {
+ public fun getChildren ()Lkotlinx/coroutines/flow/Flow;
+ public abstract fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McElementFactory {
+ public abstract fun create (Lmodulecheck/parsing/element/resolve/McElementContext;Ljava/io/File;Ljava/lang/Object;Lmodulecheck/parsing/element/McElement;)Lmodulecheck/parsing/element/McElement;
+ public abstract fun createKtFile (Lmodulecheck/parsing/element/resolve/McElementContext;Ljava/io/File;Lorg/jetbrains/kotlin/psi/KtFile;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class modulecheck/parsing/element/McElementKt {
+ public static final fun parents (Lmodulecheck/parsing/element/McElementWithParent;)Lkotlin/sequences/Sequence;
+}
+
+public abstract interface class modulecheck/parsing/element/McElementWithParent : modulecheck/parsing/element/McElement {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McExtensionElement : modulecheck/parsing/element/McCallable$McKtCallable, modulecheck/parsing/element/McKtElement {
+ public abstract fun getReceiver ()Lmodulecheck/parsing/element/McType;
+}
+
+public abstract interface class modulecheck/parsing/element/McExtensionElement$McKtExtensionFunction : modulecheck/parsing/element/McExtensionElement, modulecheck/parsing/element/McFunction$McKtFunction {
+}
+
+public abstract interface class modulecheck/parsing/element/McExtensionElement$McKtExtensionProperty : modulecheck/parsing/element/McExtensionElement, modulecheck/parsing/element/McProperty$McKtProperty {
+}
+
+public abstract interface class modulecheck/parsing/element/McFile : modulecheck/parsing/element/McElement, modulecheck/parsing/source/HasPackageName {
+ public abstract fun getApiReferences ()Ljava/util/List;
+ public abstract fun getDeclarations ()Ljava/util/List;
+ public abstract fun getDeclaredTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getDeclaredTypesAndInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getFile ()Ljava/io/File;
+ public abstract fun getImports ()Lmodulecheck/utils/lazy/LazySet$DataSource;
+ public abstract fun getReferences ()Ljava/util/List;
+ public abstract fun getWildcardImports ()Lmodulecheck/utils/lazy/LazySet$DataSource;
+}
+
+public abstract interface class modulecheck/parsing/element/McFile$McJavaFile : modulecheck/parsing/element/McFile, modulecheck/parsing/element/McJavaElement {
+}
+
+public abstract interface class modulecheck/parsing/element/McFile$McKtFile : modulecheck/parsing/element/McAnnotated, modulecheck/parsing/element/McFile, modulecheck/parsing/element/McKtElement {
+ public abstract fun getAnvilScopeArguments (Ljava/util/List;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun getDeclaredTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getDeclaredTypesAndInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getImportAliases ()Ljava/util/Map;
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtFile;
+ public abstract fun getTopLevelFunctions ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getTopLevelProperties ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public final class modulecheck/parsing/element/McFile$McKtFile$ScopeArgumentParseResult {
+ public fun (Ljava/util/Set;Ljava/util/Set;)V
+ public final fun component1 ()Ljava/util/Set;
+ public final fun component2 ()Ljava/util/Set;
+ public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lmodulecheck/parsing/element/McFile$McKtFile$ScopeArgumentParseResult;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/McFile$McKtFile$ScopeArgumentParseResult;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lmodulecheck/parsing/element/McFile$McKtFile$ScopeArgumentParseResult;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getContributeArguments ()Ljava/util/Set;
+ public final fun getMergeArguments ()Ljava/util/Set;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class modulecheck/parsing/element/McFunction : modulecheck/parsing/element/McCallable, modulecheck/parsing/element/McElement, modulecheck/parsing/element/McHasTypeParameters {
+ public abstract fun getParameters ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getProperties ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getReturnType ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public abstract fun getTypeParamters ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McFunction$McJavaFunction : modulecheck/parsing/element/McCallable$McJavaCallable, modulecheck/parsing/element/McFunction {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McJavaElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McFunction$McKtFunction : modulecheck/parsing/element/McCallable$McKtCallable, modulecheck/parsing/element/McFunction {
+ public abstract fun getParameters ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public abstract fun getProperties ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtFunction;
+ public abstract fun getReturnType ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public abstract fun getTypeParamters ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McHasTypeParameters {
+ public abstract fun getTypeParameters ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McJavaElement : modulecheck/parsing/element/McElement {
+ public abstract fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McJavaFile;
+}
+
+public abstract interface class modulecheck/parsing/element/McKtDeclaredElement : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement {
+}
+
+public abstract interface class modulecheck/parsing/element/McKtElement : modulecheck/parsing/element/McElement {
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McParameter : modulecheck/parsing/element/McCallable, modulecheck/parsing/element/McElement {
+ public abstract fun getIndex ()I
+}
+
+public abstract interface class modulecheck/parsing/element/McParameter$McJavaParameter : modulecheck/parsing/element/McCallable$McJavaCallable, modulecheck/parsing/element/McParameter {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McJavaElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McParameter$McKtParameter : modulecheck/parsing/element/McCallable$McKtCallable, modulecheck/parsing/element/McParameter {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McCallable, modulecheck/parsing/element/McElement {
+ public abstract fun getTypeReferenceName ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public abstract fun isMutable ()Z
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty$McJavaProperty : modulecheck/parsing/element/McCallable$McJavaCallable, modulecheck/parsing/element/McProperty {
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty$McJavaProperty$JavaMemberProperty : modulecheck/parsing/element/McProperty$McJavaProperty {
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty$McKtProperty : modulecheck/parsing/element/McCallable$McKtCallable, modulecheck/parsing/element/McProperty {
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtCallableDeclaration;
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty$McKtProperty$KtConstructorProperty : modulecheck/parsing/element/McProperty$McKtProperty {
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtParameter;
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty$McKtProperty$KtExtensionProperty : modulecheck/parsing/element/McHasTypeParameters, modulecheck/parsing/element/McProperty$McKtProperty$KtMemberProperty {
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtProperty;
+}
+
+public abstract interface class modulecheck/parsing/element/McProperty$McKtProperty$KtMemberProperty : modulecheck/parsing/element/McProperty$McKtProperty {
+ public abstract fun getPsi ()Lorg/jetbrains/kotlin/psi/KtProperty;
+}
+
+public abstract interface class modulecheck/parsing/element/McType : modulecheck/parsing/element/McAnnotated, modulecheck/parsing/element/McElementWithParent, modulecheck/parsing/element/McHasTypeParameters {
+ public abstract fun getSuperTypes ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McType {
+ public abstract fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public abstract fun getFunctions ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getInnerTypesRecursive ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getProperties ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McJavaConcreteType : modulecheck/parsing/element/McJavaElement, modulecheck/parsing/element/McType$McConcreteType, modulecheck/parsing/element/McType$McConcreteType$McJavaType {
+ public abstract fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McJavaFile;
+ public abstract fun getInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getInnerTypesRecursive ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McJavaConcreteType$McJavaClass : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McType$McConcreteType$McJavaConcreteType {
+ public abstract fun getConstructors ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McJavaConcreteType$McJavaInterface : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McType$McConcreteType$McJavaConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McJavaType : modulecheck/parsing/element/McJavaElement, modulecheck/parsing/element/McType {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McJavaElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType : modulecheck/parsing/element/McKtDeclaredElement, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType, modulecheck/parsing/element/McType$McConcreteType$McKtType {
+ public abstract fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public abstract fun getFunctions ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getInnerTypesRecursive ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public abstract fun getProperties ()Lmodulecheck/utils/lazy/LazySet;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtAnnotationClass : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtClass : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+ public abstract fun getConstructors ()Lmodulecheck/utils/lazy/LazySet;
+ public abstract fun getPrimaryConstructor ()Lmodulecheck/parsing/element/McFunction$McKtFunction;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtCompanionObject : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtEnum : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtInterface : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtObject : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtTypeAlias : modulecheck/parsing/element/Declared, modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McConcreteType$McKtType : modulecheck/parsing/element/McKtElement, modulecheck/parsing/element/McType {
+ public abstract fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McJavaTypeParameter : modulecheck/parsing/element/McType$McConcreteType$McJavaType, modulecheck/parsing/element/McType$McTypeParameter {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McKtTypeParameter : modulecheck/parsing/element/McType$McConcreteType$McKtType, modulecheck/parsing/element/McType$McTypeParameter {
+}
+
+public abstract interface class modulecheck/parsing/element/McType$McTypeParameter : modulecheck/parsing/element/McType {
+}
+
+public abstract interface class modulecheck/parsing/element/McVisibility {
+}
+
+public abstract interface class modulecheck/parsing/element/McVisibility$McJavaVisibility : modulecheck/parsing/element/McVisibility {
+}
+
+public final class modulecheck/parsing/element/McVisibility$McJavaVisibility$PackagePrivate : modulecheck/parsing/element/McVisibility$McJavaVisibility {
+ public static final field INSTANCE Lmodulecheck/parsing/element/McVisibility$McJavaVisibility$PackagePrivate;
+}
+
+public abstract interface class modulecheck/parsing/element/McVisibility$McKtVisibility : modulecheck/parsing/element/McVisibility {
+}
+
+public final class modulecheck/parsing/element/McVisibility$McKtVisibility$Internal : modulecheck/parsing/element/McVisibility$McKtVisibility {
+ public static final field INSTANCE Lmodulecheck/parsing/element/McVisibility$McKtVisibility$Internal;
+}
+
+public final class modulecheck/parsing/element/McVisibility$Private : modulecheck/parsing/element/McVisibility$McJavaVisibility, modulecheck/parsing/element/McVisibility$McKtVisibility {
+ public static final field INSTANCE Lmodulecheck/parsing/element/McVisibility$Private;
+}
+
+public final class modulecheck/parsing/element/McVisibility$Protected : modulecheck/parsing/element/McVisibility$McJavaVisibility, modulecheck/parsing/element/McVisibility$McKtVisibility {
+ public static final field INSTANCE Lmodulecheck/parsing/element/McVisibility$Protected;
+}
+
+public final class modulecheck/parsing/element/McVisibility$Public : modulecheck/parsing/element/McVisibility$McJavaVisibility, modulecheck/parsing/element/McVisibility$McKtVisibility {
+ public static final field INSTANCE Lmodulecheck/parsing/element/McVisibility$Public;
+}
+
+public final class modulecheck/parsing/element/TraversalKt {
+ public static final fun childrenRecursive (Lmodulecheck/parsing/element/McElement;)Lkotlinx/coroutines/flow/Flow;
+}
+
+public final class modulecheck/parsing/element/resolve/McElementContext {
+ public fun (Lmodulecheck/parsing/element/resolve/NameParser2;Lmodulecheck/parsing/element/resolve/SymbolResolver;Lmodulecheck/parsing/source/McName$CompatibleLanguage;Lmodulecheck/utils/lazy/LazyDeferred;Lkotlin/jvm/functions/Function1;)V
+ public final fun declaredNameOrNull (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public final fun getBindingContextDeferred ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public final fun getKotlinEnvironmentDeferred ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public final fun getLanguage ()Lmodulecheck/parsing/source/McName$CompatibleLanguage;
+ public final fun getNameParser ()Lmodulecheck/parsing/element/resolve/NameParser2;
+ public final fun getStdLibNameOrNull ()Lkotlin/jvm/functions/Function1;
+ public final fun getSymbolResolver ()Lmodulecheck/parsing/element/resolve/SymbolResolver;
+ public final fun resolveReferenceNameOrNull (Lmodulecheck/parsing/element/McFile;Lmodulecheck/parsing/source/ReferenceName;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class modulecheck/parsing/element/resolve/NameParser2 {
+ public abstract fun parse (Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class modulecheck/parsing/element/resolve/NameParser2$NameParser2Packet {
+ public fun (Lmodulecheck/parsing/element/McFile;Lmodulecheck/parsing/source/ReferenceName;Lmodulecheck/parsing/source/McName$CompatibleLanguage;Lkotlin/jvm/functions/Function1;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/McFile;
+ public final fun component2 ()Lmodulecheck/parsing/source/ReferenceName;
+ public final fun component3 ()Lmodulecheck/parsing/source/McName$CompatibleLanguage;
+ public final fun component4 ()Lkotlin/jvm/functions/Function1;
+ public final fun copy (Lmodulecheck/parsing/element/McFile;Lmodulecheck/parsing/source/ReferenceName;Lmodulecheck/parsing/source/McName$CompatibleLanguage;Lkotlin/jvm/functions/Function1;)Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;Lmodulecheck/parsing/element/McFile;Lmodulecheck/parsing/source/ReferenceName;Lmodulecheck/parsing/source/McName$CompatibleLanguage;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getFile ()Lmodulecheck/parsing/element/McFile;
+ public final fun getReferenceLanguage ()Lmodulecheck/parsing/source/McName$CompatibleLanguage;
+ public final fun getStdLibNameOrNull ()Lkotlin/jvm/functions/Function1;
+ public final fun getToResolve ()Lmodulecheck/parsing/source/ReferenceName;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/parsing/element/resolve/ParsingChain2 : modulecheck/parsing/element/resolve/ParsingInterceptor2$Chain {
+ public synthetic fun (Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;Ljava/util/List;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun getPacket ()Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;
+ public fun proceed (Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class modulecheck/parsing/element/resolve/ParsingChain2$Factory : modulecheck/parsing/element/resolve/NameParser2 {
+ public fun (Ljava/util/List;)V
+ public fun parse (Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class modulecheck/parsing/element/resolve/ParsingInterceptor2 {
+ public abstract fun intercept (Lmodulecheck/parsing/element/resolve/ParsingInterceptor2$Chain;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class modulecheck/parsing/element/resolve/ParsingInterceptor2$Chain {
+ public abstract fun getPacket ()Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;
+ public abstract fun proceed (Lmodulecheck/parsing/element/resolve/NameParser2$NameParser2Packet;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class modulecheck/parsing/element/resolve/SymbolResolver {
+ public abstract fun declaredNameOrNull (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
diff --git a/modulecheck-parsing/element/api/build.gradle.kts b/modulecheck-parsing/element/api/build.gradle.kts
new file mode 100644
index 0000000000..11b2947395
--- /dev/null
+++ b/modulecheck-parsing/element/api/build.gradle.kts
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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.
+ */
+
+plugins {
+ id("mcbuild")
+}
+
+mcbuild {
+ published(artifactId = "modulecheck-parsing-element-api")
+ anvil()
+}
+
+dependencies {
+ api(libs.kotlin.compiler)
+ api(libs.semVer)
+
+ api(project(path = ":modulecheck-parsing:kotlin-compiler:api"))
+ api(project(path = ":modulecheck-parsing:source:api"))
+ api(project(path = ":modulecheck-utils:lazy"))
+
+ implementation(project(path = ":modulecheck-name:api"))
+ implementation(project(path = ":modulecheck-utils:coroutines:api"))
+
+ testImplementation(libs.bundles.junit)
+ testImplementation(libs.bundles.kotest)
+ testImplementation(libs.kotlin.reflect)
+
+ testImplementation(project(path = ":modulecheck-internal-testing"))
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McCallable.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McCallable.kt
new file mode 100644
index 0000000000..6c02237a59
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McCallable.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import modulecheck.name.TypeName
+import modulecheck.parsing.element.McCallable.McJavaCallable
+import modulecheck.parsing.element.McCallable.McKtCallable
+import modulecheck.parsing.element.McFunction.McKtFunction
+import modulecheck.parsing.element.McParameter.McKtParameter
+import modulecheck.parsing.element.McProperty.McKtProperty
+import modulecheck.parsing.element.McType.McKtTypeParameter
+import modulecheck.parsing.element.McType.McTypeParameter
+import modulecheck.parsing.element.McVisibility.McJavaVisibility
+import modulecheck.parsing.element.McVisibility.McKtVisibility
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.LazySet
+import org.jetbrains.kotlin.psi.KtCallableDeclaration
+import org.jetbrains.kotlin.psi.KtFunction
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtProperty
+
+/** A sealed interface representing a callable element in the codebase. */
+sealed interface McCallable :
+ McElement,
+ McElementWithParent,
+ HasVisibility,
+ McAnnotated {
+
+ /** A sealed interface representing a Java callable element. */
+ sealed interface McJavaCallable : McCallable, McJavaElement {
+ override val parent: McJavaElement
+ override val visibility: McJavaVisibility
+ }
+
+ /** A sealed interface representing a Kotlin callable element. */
+ sealed interface McKtCallable : McCallable, McKtElement {
+ override val psi: KtCallableDeclaration
+ override val parent: McKtElement
+ override val visibility: McKtVisibility
+ }
+}
+
+/** A sealed interface representing a property element in the codebase. */
+sealed interface McProperty : McCallable, McElement, Declared {
+
+ /** A deferred property representing the type name of the property. */
+ val typeReferenceName: LazyDeferred
+
+ /** Flag indicating whether the property is mutable. */
+ val isMutable: Boolean
+
+ /** A sealed interface representing a Java property element. */
+ sealed interface McJavaProperty : McProperty, McJavaCallable {
+ /** An interface representing a Java member property. */
+ interface JavaMemberProperty : McJavaProperty
+ }
+
+ /** A sealed interface representing a Kotlin property element. */
+ sealed interface McKtProperty : McProperty, McKtCallable {
+ override val psi: KtCallableDeclaration
+
+ /** A sealed interface representing a Kotlin member property element. */
+ interface KtMemberProperty : McKtProperty {
+ override val psi: KtProperty
+ }
+
+ /** A sealed interface representing a Kotlin extension property element. */
+ interface KtExtensionProperty : KtMemberProperty, McHasTypeParameters {
+ override val psi: KtProperty
+ }
+
+ /** A sealed interface representing a Kotlin constructor property element. */
+ interface KtConstructorProperty : McKtProperty {
+ override val psi: KtParameter
+ }
+ }
+}
+
+/** A sealed interface representing a parameter element in the codebase. */
+sealed interface McParameter : McCallable, McElement {
+ /** The index of the parameter. */
+ val index: Int
+
+ /** A sealed interface representing a Java parameter element. */
+ interface McJavaParameter : McParameter, McJavaCallable {
+ override val parent: McJavaElement
+ }
+
+ /** A sealed interface representing a Kotlin parameter element. */
+ interface McKtParameter : McParameter, McKtCallable {
+ override val parent: McKtElement
+ }
+}
+
+/** A sealed interface representing a function element in the codebase. */
+sealed interface McFunction : McCallable, McElement, McHasTypeParameters {
+
+ /** A lazy set of parameters for the function. */
+ val parameters: LazySet
+
+ /** A lazy set of properties for the function. */
+ val properties: LazySet
+
+ /** A deferred property representing the return type of the function. */
+ val returnType: LazyDeferred
+
+ /** A lazy set of type parameters for the function. */
+ val typeParamters: LazySet
+
+ /** A sealed interface representing a Java function element. */
+ interface McJavaFunction : McFunction, McJavaCallable {
+ override val parent: McJavaElement
+ }
+
+ /** represents a Kotlin function element. */
+ interface McKtFunction : McFunction, McKtCallable {
+ override val psi: KtFunction
+ override val parent: McKtElement
+ override val parameters: LazySet
+ override val properties: LazySet
+ override val returnType: LazyDeferred
+ override val typeParamters: LazySet
+ }
+}
+
+/** A sealed interface representing an extension element in the codebase. */
+sealed interface McExtensionElement : McKtCallable, McKtElement {
+ /** The receiver type. */
+ val receiver: McType
+
+ /** A sealed interface representing a Kotlin extension property. */
+ interface McKtExtensionProperty : McExtensionElement, McKtProperty
+
+ /** A sealed interface representing a Kotlin extension function. */
+ interface McKtExtensionFunction : McExtensionElement, McKtFunction
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McElement.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McElement.kt
new file mode 100644
index 0000000000..3c368172ea
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McElement.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import modulecheck.parsing.element.McFile.McJavaFile
+import modulecheck.parsing.element.McFile.McKtFile
+import modulecheck.parsing.element.McType.McConcreteType
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType
+import modulecheck.parsing.element.McType.McTypeParameter
+import modulecheck.parsing.source.DeclaredName
+import modulecheck.parsing.source.HasPackageName
+import modulecheck.parsing.source.HasSimpleNames
+import modulecheck.parsing.source.RawAnvilAnnotatedType
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.LazySet
+import modulecheck.utils.lazy.LazySet.DataSource
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+
+/** An element that has been declared, it can be a class, function, variable, etc. */
+interface Declared :
+ HasPackageName,
+ HasSimpleNames {
+ /** The name of this declared element. */
+ val declaredName: DeclaredName
+
+ /** A boolean indicating if this is an API element or not. */
+ val isApi: Boolean get() = false
+}
+
+/**
+ * Base interface for all code elements parsed from source
+ * files. This includes classes, functions, variables, etc.
+ */
+sealed interface McElement {
+
+ /** The PSI element representing the physical code element in the source code. */
+ val psi: PsiElement
+
+ /** The file that contains this element. */
+ val containingFile: McFile
+
+ /** The children elements of this element. */
+ val children: Flow get() = flowOf()
+}
+
+/** Represents a Java element in the source code. */
+sealed interface McJavaElement : McElement {
+ /** The Java file that contains this element. */
+ override val containingFile: McJavaFile
+}
+
+/** Represents a Kotlin element in the source code. */
+sealed interface McKtElement : McElement {
+
+ override val psi: KtElement
+
+ override val containingFile: McFile.McKtFile
+ get() = when (this) {
+
+ is McElementWithParent<*> -> (parent as McKtElement).containingFile
+
+ is McKtFile -> this
+
+ else -> throw IllegalArgumentException(
+
+ "How did you call `containingFile` without being `McElementWithParent` or `McKtFile`?"
+
+ )
+ }
+}
+
+/** Represents a declared Kotlin element in the source code. */
+sealed interface McKtDeclaredElement : McKtElement, Declared
+
+/** Represents an element with a parent element. */
+sealed interface McElementWithParent : McElement {
+ /** The parent element */
+ val parent: E
+}
+
+/**
+ * Generates a sequence of parent elements.
+ *
+ * @receiver An element with a parent.
+ * @return A sequence of parent elements.
+ */
+fun McElementWithParent<*>.parents(): Sequence {
+
+ return generateSequence(this) { element ->
+
+ (element as? McElementWithParent<*>)?.parent
+ }
+}
+
+/** Represents an annotated element. */
+interface McAnnotated {
+
+ /** The annotations of this element. */
+ val annotations: LazySet
+}
+
+/** Represents an element with type parameters. */
+interface McHasTypeParameters {
+
+ /** The type parameters of this element. */
+ val typeParameters: LazySet
+}
+
+/** Represents an annotation. */
+interface McAnnotation : McElement, McElementWithParent {
+
+ /** The reference name of this annotation. */
+ val referenceName: LazyDeferred
+
+ interface McKtAnnotation :
+
+ McKtElement,
+
+ McElementWithParent,
+
+ McAnnotation {
+
+ override val parent: McKtElement
+ }
+}
+
+/** Represents an argument of an annotation. */
+interface McAnnotationArgument : McElement, McElementWithParent {
+
+ interface McKtAnnotationArgument :
+ McKtElement,
+ McElementWithParent,
+ McAnnotationArgument {
+
+ override val parent: McKtElement
+ }
+
+ /** The type of this argument. */
+ val type: LazyDeferred
+
+ /** The value of this argument. */
+ val value: Any
+}
+
+/** Represents a file. */
+sealed interface McFile : McElement, HasPackageName {
+ /** The actual file. */
+ val file: File
+
+ /** The imports in this file. */
+ val imports: DataSource
+
+ /** The API references in this file. */
+ val apiReferences: List>
+
+ /** The references in this file. */
+ val references: List>
+
+ /** The declarations in this file. */
+ val declarations: List>
+
+ /** The declared types in this file. */
+ val declaredTypes: LazySet
+
+ /** The declared types and inner types in this file. */
+ val declaredTypesAndInnerTypes: LazySet
+
+ /** Represents a single Kotlin file. */
+ interface McKtFile :
+ McFile,
+ McKtElement,
+ McAnnotated {
+
+ override val psi: KtFile
+
+ override val declaredTypes: LazySet
+
+ override val declaredTypesAndInnerTypes: LazySet
+
+ /** The top level functions in this file. */
+ val topLevelFunctions: LazySet
+
+ /** The top level properties in this file. */
+ val topLevelProperties: LazySet
+
+ /** The import aliases in this file. */
+ val importAliases: Map
+
+ /** A weird, dated function for getting Anvil scope arguments */
+ suspend fun getAnvilScopeArguments(
+ allAnnotations: List,
+ mergeAnnotations: List
+ ): ScopeArgumentParseResult
+
+ /**
+ * Represents the parsed results for Anvil scope arguments.
+ *
+ * @property mergeArguments The set of merge arguments derived from Anvil annotations.
+ * @property contributeArguments The set of contribute arguments derived from Anvil annotations.
+ */
+ data class ScopeArgumentParseResult(
+ val mergeArguments: Set,
+ val contributeArguments: Set
+ )
+ }
+
+ /** Represents a Java file. */
+ interface McJavaFile : McFile, McJavaElement
+
+ /** The wildcard imports in this file. */
+ val wildcardImports: DataSource
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McElementFactory.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McElementFactory.kt
new file mode 100644
index 0000000000..c604b6a57c
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McElementFactory.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import modulecheck.parsing.element.McFile.McKtFile
+import modulecheck.parsing.element.resolve.McElementContext
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+
+/** Creates an [McElement] */
+interface McElementFactory {
+ /**
+ * @param context the context from which symbols should be resolved
+ * @param fileSystemFile the java.io.File containing this element
+ * @param backingElement the AST symbol used for actual parsing
+ * @return a KtFile for this [backingElement]
+ */
+ suspend fun createKtFile(
+ context: McElementContext,
+ fileSystemFile: File,
+ backingElement: KtFile
+ ): McKtFile
+
+ /**
+ * @param context the context from which symbols should be resolved
+ * @param fileSystemFile the java.io.File containing this element
+ * @param backingElement the AST symbol used for actual parsing
+ * @param parent the parent element for this new element
+ * @return some subtype of [McElement] which wraps [backingElement]
+ */
+ fun create(
+ context: McElementContext,
+ fileSystemFile: File,
+ backingElement: T,
+ parent: McElement
+ ): McElement
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McType.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McType.kt
new file mode 100644
index 0000000000..f6cfd952c4
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McType.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import modulecheck.parsing.element.McFile.McJavaFile
+import modulecheck.parsing.element.McFile.McKtFile
+import modulecheck.parsing.element.McFunction.McKtFunction
+import modulecheck.parsing.element.McProperty.McKtProperty
+import modulecheck.parsing.element.McType.McConcreteType.McJavaType
+import modulecheck.parsing.element.McType.McConcreteType.McKtType
+import modulecheck.utils.lazy.LazySet
+
+sealed interface McType : McElementWithParent, McAnnotated, McHasTypeParameters {
+
+ /**
+ * In a concrete type, this represents super-classes and interfaces.
+ *
+ * In a generic type, supers are the upper bound(s).
+ */
+ val superTypes: LazySet
+
+ /** Represents a class, interface, object, or companion object */
+ sealed interface McConcreteType : McType, Declared {
+
+ override val containingFile: McFile
+
+ val innerTypes: LazySet
+ val innerTypesRecursive: LazySet
+ val properties: LazySet
+ val functions: LazySet
+
+ interface McJavaType : McType, McJavaElement {
+ override val parent: McJavaElement
+ }
+
+ interface McKtType : McType, McKtElement {
+ override val parent: McKtElement
+ }
+
+ sealed interface McJavaConcreteType : McConcreteType, McJavaType, McJavaElement {
+ override val innerTypes: LazySet
+ override val innerTypesRecursive: LazySet
+
+ override val containingFile: McJavaFile
+
+ interface McJavaInterface : McJavaConcreteType, Declared
+ interface McJavaClass : McJavaConcreteType, Declared {
+
+ val constructors: LazySet
+ }
+ }
+
+ interface McKtConcreteType :
+ McKtType,
+ McConcreteType,
+ McKtDeclaredElement,
+ McKtElement {
+ override val parent: McKtElement
+ override val innerTypes: LazySet
+ override val innerTypesRecursive: LazySet
+
+ override val containingFile: McKtFile
+ override val properties: LazySet
+ override val functions: LazySet
+
+ interface McKtAnnotationClass : McKtConcreteType, McKtElement, Declared
+ interface McKtClass : McKtConcreteType, McKtElement, Declared {
+
+ val primaryConstructor: McFunction.McKtFunction?
+
+ /** All constructors, including the primary if it exists */
+ val constructors: LazySet
+ }
+
+ interface McKtCompanionObject : McKtConcreteType, McKtElement, Declared
+ interface McKtTypeAlias : McKtConcreteType, McKtElement, Declared
+ interface McKtEnum : McKtConcreteType, McKtElement, Declared
+ interface McKtInterface : McKtConcreteType, McKtElement, Declared
+ interface McKtObject : McKtConcreteType, McKtElement, Declared
+ }
+ }
+
+ /** Represents a generic type used as a parameter, like `` or ``. */
+ interface McTypeParameter : McType
+ interface McJavaTypeParameter : McTypeParameter, McJavaType
+ interface McKtTypeParameter : McTypeParameter, McKtType
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McVisibility.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McVisibility.kt
new file mode 100644
index 0000000000..23f2b8ddcf
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/McVisibility.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import modulecheck.parsing.element.McVisibility.McJavaVisibility
+import modulecheck.parsing.element.McVisibility.McKtVisibility
+
+interface HasVisibility {
+ val visibility: McVisibility
+}
+
+interface HasJavaVisibility : HasVisibility {
+ override val visibility: McJavaVisibility
+}
+
+interface HasKtVisibility : HasVisibility {
+ override val visibility: McKtVisibility
+}
+
+sealed interface McVisibility {
+ sealed interface McJavaVisibility : McVisibility {
+ object PackagePrivate : McJavaVisibility
+ }
+
+ sealed interface McKtVisibility : McVisibility {
+
+ object Internal : McKtVisibility
+ }
+
+ object Public : McJavaVisibility, McKtVisibility
+ object Protected : McJavaVisibility, McKtVisibility
+ object Private : McJavaVisibility, McKtVisibility
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/resolve/McElementContext.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/resolve/McElementContext.kt
new file mode 100644
index 0000000000..54f05d3147
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/resolve/McElementContext.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.resolve
+
+import modulecheck.parsing.element.McFile
+import modulecheck.parsing.element.resolve.NameParser2.NameParser2Packet
+import modulecheck.parsing.kotlin.compiler.KotlinEnvironment
+import modulecheck.parsing.source.McName.CompatibleLanguage
+import modulecheck.parsing.source.QualifiedDeclaredName
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.lazyDeferred
+
+/**
+ * Provides a context for parsing and resolving elements in a module check system.
+ * This class is designed to work with any type `T` that represents a symbol in the
+ * system. It uses a [NameParser2] to parse names, a [SymbolResolver] to resolve
+ * symbols, and a [KotlinEnvironment] to provide a context for Kotlin language features.
+ *
+ * @property nameParser The parser used to parse names in the system.
+ * @property symbolResolver The resolver used to resolve symbols in the system.
+ * @property language The language that is compatible with the system.
+ * @property kotlinEnvironmentDeferred A deferred [KotlinEnvironment]
+ * that provides a context for Kotlin language features.
+ * @property stdLibNameOrNull A function that takes a [ReferenceName] and returns a
+ * [QualifiedDeclaredName] from the standard library, or null if no such name exists.
+ */
+class McElementContext(
+ val nameParser: NameParser2,
+ val symbolResolver: SymbolResolver,
+ val language: CompatibleLanguage,
+ val kotlinEnvironmentDeferred: LazyDeferred,
+ val stdLibNameOrNull: ReferenceName.() -> QualifiedDeclaredName?
+) {
+
+ /**
+ * A deferred binding context obtained from the [KotlinEnvironment].
+ * This context is used to resolve bindings in the system.
+ */
+ val bindingContextDeferred = lazyDeferred {
+ kotlinEnvironmentDeferred.await()
+ .bindingContextDeferred.await()
+ }
+
+ /**
+ * Resolves the declared name of a symbol in the system. This method is not yet implemented.
+ *
+ * @param symbol The symbol whose declared name is to be resolved.
+ * @return The declared name of the symbol, or null if the symbol does not have a declared name.
+ */
+ suspend fun declaredNameOrNull(symbol: T): QualifiedDeclaredName? {
+ TODO()
+ }
+
+ /**
+ * Resolves a reference name in a given file. This method
+ * uses the [nameParser] to parse the reference name.
+ *
+ * @param file The file in which the reference name is to be resolved.
+ * @param toResolve The reference name to resolve.
+ * @return The resolved reference name, or null if the reference name could not be resolved.
+ */
+ suspend fun resolveReferenceNameOrNull(file: McFile, toResolve: ReferenceName): ReferenceName? {
+
+ return nameParser.parse(
+ NameParser2Packet(
+ file = file,
+ toResolve = toResolve,
+ referenceLanguage = language,
+ stdLibNameOrNull = stdLibNameOrNull
+ )
+ )
+ }
+}
+
+/**
+ * Represents a resolver that can resolve symbols in the system. The type
+ * `T` represents the type of the symbols that this resolver can handle.
+ */
+fun interface SymbolResolver {
+ /**
+ * Resolves the declared name of a symbol in the system.
+ *
+ * @param symbol The symbol whose declared name is to be resolved.
+ * @return The declared name of the symbol, or null if the symbol does not have a declared name.
+ */
+ suspend fun declaredNameOrNull(symbol: T): QualifiedDeclaredName?
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/resolve/NameParser2.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/resolve/NameParser2.kt
new file mode 100644
index 0000000000..d467634336
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/resolve/NameParser2.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.resolve
+
+import modulecheck.parsing.element.McFile
+import modulecheck.parsing.element.resolve.NameParser2.NameParser2Packet
+import modulecheck.parsing.source.McName
+import modulecheck.parsing.source.QualifiedDeclaredName
+import modulecheck.parsing.source.ReferenceName
+
+/**
+ * Intercepts parsing operations. Implementations of this interface should
+ * provide a way to parse a given `NameParser2Packet` into a `ReferenceName`.
+ */
+fun interface NameParser2 {
+ /**
+ * Parses the given packet into a `ReferenceName`.
+ *
+ * @param packet The packet to parse.
+ * @return The parsed `ReferenceName`, or `null` if parsing was unsuccessful.
+ */
+ suspend fun parse(packet: NameParser2Packet): ReferenceName?
+
+ /**
+ * @property file The file being parsed.
+ * @property toResolve The reference name to be resolved.
+ * @property referenceLanguage The language of the file (Java or Kotlin).
+ * @property stdLibNameOrNull A function that returns a `QualifiedDeclaredName` if the
+ * receiver name is part of the stdlib of this `referenceLanguage`, otherwise null.
+ */
+ data class NameParser2Packet(
+ val file: McFile,
+ val toResolve: ReferenceName,
+ val referenceLanguage: McName.CompatibleLanguage,
+ val stdLibNameOrNull: ReferenceName.() -> QualifiedDeclaredName?
+ )
+}
+
+/**
+ * Intercepts parsing operations. Implementations of this interface should provide
+ * a way to intercept the parsing process and potentially modify the result.
+ */
+fun interface ParsingInterceptor2 {
+
+ /**
+ * Intercepts the parsing process.
+ *
+ * @param chain The chain of parsing operations.
+ * @return The intercepted `ReferenceName`, or `null` if the interception was unsuccessful.
+ */
+ suspend fun intercept(chain: Chain): ReferenceName?
+
+ /** Represents a chain of parsing operations. */
+ interface Chain {
+ /** */
+ val packet: NameParser2Packet
+
+ /**
+ * Passes the `packet` argument on to the next interceptor in this chain.
+ *
+ * @param packet The packet to pass on.
+ * @return The result of the next interceptor in the
+ * chain, or `null` if there are no more interceptors.
+ */
+ suspend fun proceed(packet: NameParser2Packet): ReferenceName?
+ }
+}
+
+/**
+ * Represents a chain of parsing operations.
+ *
+ * @property packet The packet to be parsed.
+ * @property interceptors The list of interceptors in the chain.
+ */
+class ParsingChain2 private constructor(
+ override val packet: NameParser2Packet,
+ private val interceptors: List
+) : ParsingInterceptor2.Chain {
+
+ /**
+ * Passes the `packet` argument on to the next interceptor in this chain.
+ *
+ * @param packet The packet to pass on.
+ * @return The result of the next interceptor in the
+ * chain, or `null` if there are no more interceptors.
+ */
+ override suspend fun proceed(packet: NameParser2Packet): ReferenceName? {
+ val next = ParsingChain2(packet, interceptors.drop(1))
+
+ val interceptor = interceptors.first()
+
+ return interceptor.intercept(next)
+ }
+
+ /**
+ * Factory for creating instances of `ParsingChain2`.
+ *
+ * @property interceptors The list of interceptors to include in the chain.
+ */
+ class Factory(
+ private val interceptors: List
+ ) : NameParser2 {
+
+ /**
+ * Parses the given packet into a `ReferenceName` using the chain of interceptors.
+ *
+ * @param packet The packet to parse.
+ * @return The parsed `ReferenceName`, or `null` if parsing was unsuccessful.
+ */
+ override suspend fun parse(packet: NameParser2Packet): ReferenceName? {
+
+ return ParsingChain2(packet, interceptors).proceed(packet)
+ }
+ }
+}
diff --git a/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/traversal.kt b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/traversal.kt
new file mode 100644
index 0000000000..472ac6f82c
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/main/kotlin/modulecheck/parsing/element/traversal.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.flowOf
+import modulecheck.utils.coroutines.distinct
+import modulecheck.utils.coroutines.plus
+
+fun McElement.childrenRecursive(): Flow {
+ return flowOf(this)
+ .plus(
+ children.flatMapConcat { child ->
+ child.childrenRecursive()
+ }
+ )
+ .distinct()
+}
+
+inline fun McElement.childrenOfTypeRecursive(): Flow {
+ return childrenRecursive().filterIsInstance()
+}
diff --git a/modulecheck-parsing/element/api/src/test/kotlin/modulecheck/parsing/element/McElementTest.kt b/modulecheck-parsing/element/api/src/test/kotlin/modulecheck/parsing/element/McElementTest.kt
new file mode 100644
index 0000000000..c4b7823ed3
--- /dev/null
+++ b/modulecheck-parsing/element/api/src/test/kotlin/modulecheck/parsing/element/McElementTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import io.kotest.assertions.asClue
+import io.kotest.inspectors.forAll
+import io.kotest.matchers.reflection.shouldBeOfType
+import io.kotest.matchers.reflection.shouldBeSubtypeOf
+import io.kotest.matchers.reflection.shouldHaveMemberProperty
+import io.kotest.matchers.reflection.shouldNotHaveMemberProperty
+import modulecheck.testing.BaseTest
+import modulecheck.testing.TestEnvironment
+import modulecheck.testing.sealedSubclassesRecursive
+import org.junit.jupiter.api.Test
+import kotlin.reflect.full.isSubclassOf
+
+class McElementTest : BaseTest() {
+
+ @Test
+ fun `every McElement type except files should have a 'parent' property`() {
+
+ // The language-specific element types are technically subclasses,
+ // but also can't have parents.
+ val excluded = setOf(
+ McJavaElement::class,
+ McKtElement::class,
+ McKtDeclaredElement::class
+ )
+
+ McElement::class.sealedSubclassesRecursive()
+ .filterNot { it in excluded }
+ .forAll { sub ->
+
+ when {
+ sub.isSubclassOf(McFile::class) -> {
+ sub shouldNotHaveMemberProperty "parent"
+ }
+
+ sub.isSubclassOf(McJavaElement::class) ->
+ "java element types should have java parents".asClue {
+ sub.shouldBeSubtypeOf>()
+ sub.shouldHaveMemberProperty("parent") { property ->
+ property.returnType.shouldBeOfType()
+ }
+ }
+
+ sub.isSubclassOf(McKtElement::class) ->
+ "kotlin element types should have kotlin parents".asClue {
+ sub.shouldBeSubtypeOf>()
+ sub.shouldHaveMemberProperty("parent") { property ->
+ property.returnType.shouldBeOfType()
+ }
+ }
+
+ else ->
+ "base element types should have agnostic parents".asClue {
+ sub.shouldBeSubtypeOf>()
+ sub.shouldHaveMemberProperty("parent") { property ->
+ property.returnType.shouldBeOfType()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/api/impl-kotlin.api b/modulecheck-parsing/element/impl-kotlin/api/impl-kotlin.api
new file mode 100644
index 0000000000..6f00fae6b8
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/api/impl-kotlin.api
@@ -0,0 +1,273 @@
+public final class modulecheck/parsing/element/McElementFactoryImpl : modulecheck/parsing/element/McElementFactory {
+ public fun ()V
+ public fun create (Lmodulecheck/parsing/element/resolve/McElementContext;Ljava/io/File;Ljava/lang/Object;Lmodulecheck/parsing/element/McElement;)Lmodulecheck/parsing/element/McElement;
+ public fun createKtFile (Lmodulecheck/parsing/element/resolve/McElementContext;Ljava/io/File;Lorg/jetbrains/kotlin/psi/KtFile;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract class modulecheck/parsing/element/kotlin/AbstractMcKtConcreteType : modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType, modulecheck/parsing/element/McType$McConcreteType$McKtType, modulecheck/parsing/element/kotlin/HasMcElementContext {
+ public fun getAnnotations ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public fun getDeclaredName ()Lmodulecheck/parsing/source/DeclaredName;
+ public fun getFunctions ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getInnerTypesRecursive ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getPackageName ()Lmodulecheck/parsing/source/PackageName;
+ public fun getProperties ()Lmodulecheck/utils/lazy/LazySet;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClassOrObject;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getSimpleNames ()Ljava/util/List;
+ public fun getSuperTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getTypeParameters ()Lmodulecheck/utils/lazy/LazySet;
+ public final fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class modulecheck/parsing/element/kotlin/HasMcElementContext {
+ public fun bindingContext (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public fun bindingContext (Lorg/jetbrains/kotlin/util/slicedMap/ReadOnlySlice;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static synthetic fun bindingContext$suspendImpl (Lmodulecheck/parsing/element/kotlin/HasMcElementContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static synthetic fun bindingContext$suspendImpl (Lmodulecheck/parsing/element/kotlin/HasMcElementContext;Lorg/jetbrains/kotlin/util/slicedMap/ReadOnlySlice;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtAnnotationArgumentImpl : modulecheck/parsing/element/McAnnotationArgument$McKtAnnotationArgument {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtValueArgument;Lmodulecheck/parsing/element/McKtElement;)V
+ public final fun component2 ()Lorg/jetbrains/kotlin/psi/KtValueArgument;
+ public final fun component3 ()Lmodulecheck/parsing/element/McKtElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtValueArgument;Lmodulecheck/parsing/element/McKtElement;)Lmodulecheck/parsing/element/kotlin/McKtAnnotationArgumentImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtAnnotationArgumentImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtValueArgument;Lmodulecheck/parsing/element/McKtElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtAnnotationArgumentImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtValueArgument;
+ public fun getType ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public fun getValue ()Ljava/lang/Object;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtAnnotationImpl : modulecheck/parsing/element/McAnnotation$McKtAnnotation {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtAnnotationEntry;Lmodulecheck/parsing/element/McKtElement;)V
+ public final fun component2 ()Lorg/jetbrains/kotlin/psi/KtAnnotationEntry;
+ public final fun component3 ()Lmodulecheck/parsing/element/McKtElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtAnnotationEntry;Lmodulecheck/parsing/element/McKtElement;)Lmodulecheck/parsing/element/kotlin/McKtAnnotationImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtAnnotationImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtAnnotationEntry;Lmodulecheck/parsing/element/McKtElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtAnnotationImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtAnnotationEntry;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getReferenceName ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtClassImpl : modulecheck/parsing/element/kotlin/AbstractMcKtConcreteType, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtClass {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtClass;Lmodulecheck/parsing/element/McKtElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public final fun component3 ()Lorg/jetbrains/kotlin/psi/KtClass;
+ public final fun component4 ()Lmodulecheck/parsing/element/McKtElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtClass;Lmodulecheck/parsing/element/McKtElement;)Lmodulecheck/parsing/element/kotlin/McKtClassImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtClassImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtClass;Lmodulecheck/parsing/element/McKtElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtClassImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getConstructors ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public fun getPrimaryConstructor ()Lmodulecheck/parsing/element/McFunction$McKtFunction;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClass;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClassOrObject;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun hashCode ()I
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtCompanionObjectImpl : modulecheck/parsing/element/kotlin/AbstractMcKtConcreteType, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtCompanionObject {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;Lmodulecheck/parsing/element/McKtElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public final fun component3 ()Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;
+ public final fun component4 ()Lmodulecheck/parsing/element/McKtElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;Lmodulecheck/parsing/element/McKtElement;)Lmodulecheck/parsing/element/kotlin/McKtCompanionObjectImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtCompanionObjectImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;Lmodulecheck/parsing/element/McKtElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtCompanionObjectImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClassOrObject;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;
+ public fun hashCode ()I
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtConstructorPropertyImpl : modulecheck/parsing/element/Declared, modulecheck/parsing/element/HasKtVisibility, modulecheck/parsing/element/McProperty$McKtProperty$KtConstructorProperty, modulecheck/parsing/element/kotlin/HasMcElementContext {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtParameter;Lmodulecheck/parsing/element/McKtDeclaredElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lorg/jetbrains/kotlin/psi/KtParameter;
+ public final fun component3 ()Lmodulecheck/parsing/element/McKtDeclaredElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtParameter;Lmodulecheck/parsing/element/McKtDeclaredElement;)Lmodulecheck/parsing/element/kotlin/McKtConstructorPropertyImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtConstructorPropertyImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtParameter;Lmodulecheck/parsing/element/McKtDeclaredElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtConstructorPropertyImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getAnnotations ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public fun getDeclaredName ()Lmodulecheck/parsing/source/DeclaredName;
+ public fun getPackageName ()Lmodulecheck/parsing/source/PackageName;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtDeclaredElement;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtCallableDeclaration;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtParameter;
+ public fun getSimpleNames ()Ljava/util/List;
+ public fun getSimplestName-Jf_0vz4 ()Ljava/lang/String;
+ public fun getTypeReferenceName ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McKtVisibility;
+ public synthetic fun getVisibility ()Lmodulecheck/parsing/element/McVisibility;
+ public fun hashCode ()I
+ public fun isApi ()Z
+ public fun isMutable ()Z
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtFileImpl : modulecheck/parsing/element/McFile$McKtFile {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Ljava/io/File;Lorg/jetbrains/kotlin/psi/KtFile;)V
+ public fun getAnnotations ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getAnvilScopeArguments (Ljava/util/List;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public fun getApiReferences ()Ljava/util/List;
+ public fun getChildren ()Lkotlinx/coroutines/flow/Flow;
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public final fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public fun getDeclarations ()Ljava/util/List;
+ public fun getDeclaredTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getDeclaredTypesAndInnerTypes ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getFile ()Ljava/io/File;
+ public fun getImportAliases ()Ljava/util/Map;
+ public fun getImports ()Lmodulecheck/utils/lazy/LazySet$DataSource;
+ public fun getPackageName ()Lmodulecheck/parsing/source/PackageName;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtFile;
+ public fun getReferences ()Ljava/util/List;
+ public fun getTopLevelFunctions ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getTopLevelProperties ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getWildcardImports ()Lmodulecheck/utils/lazy/LazySet$DataSource;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtFunctionImpl : modulecheck/parsing/element/Declared, modulecheck/parsing/element/HasKtVisibility, modulecheck/parsing/element/McFunction$McKtFunction, modulecheck/parsing/element/kotlin/HasMcElementContext {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtFunction;Lmodulecheck/parsing/element/McKtDeclaredElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lorg/jetbrains/kotlin/psi/KtFunction;
+ public final fun component3 ()Lmodulecheck/parsing/element/McKtDeclaredElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtFunction;Lmodulecheck/parsing/element/McKtDeclaredElement;)Lmodulecheck/parsing/element/kotlin/McKtFunctionImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtFunctionImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtFunction;Lmodulecheck/parsing/element/McKtDeclaredElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtFunctionImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getAnnotations ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public fun getDeclaredName ()Lmodulecheck/parsing/source/DeclaredName;
+ public fun getPackageName ()Lmodulecheck/parsing/source/PackageName;
+ public fun getParameters ()Lmodulecheck/utils/lazy/LazySet;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtDeclaredElement;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public fun getProperties ()Lmodulecheck/utils/lazy/LazySet;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtCallableDeclaration;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtFunction;
+ public fun getReturnType ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public fun getSimpleNames ()Ljava/util/List;
+ public fun getSimplestName-Jf_0vz4 ()Ljava/lang/String;
+ public fun getTypeParameters ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getTypeParamters ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McKtVisibility;
+ public synthetic fun getVisibility ()Lmodulecheck/parsing/element/McVisibility;
+ public fun hashCode ()I
+ public fun isApi ()Z
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtInterfaceImpl : modulecheck/parsing/element/kotlin/AbstractMcKtConcreteType, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtInterface {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtClass;Lmodulecheck/parsing/element/McKtElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public final fun component3 ()Lorg/jetbrains/kotlin/psi/KtClass;
+ public final fun component4 ()Lmodulecheck/parsing/element/McKtElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtClass;Lmodulecheck/parsing/element/McKtElement;)Lmodulecheck/parsing/element/kotlin/McKtInterfaceImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtInterfaceImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtClass;Lmodulecheck/parsing/element/McKtElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtInterfaceImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClass;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClassOrObject;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun hashCode ()I
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtMemberPropertyImpl : modulecheck/parsing/element/Declared, modulecheck/parsing/element/HasKtVisibility, modulecheck/parsing/element/McProperty$McKtProperty$KtMemberProperty, modulecheck/parsing/element/kotlin/HasMcElementContext {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtProperty;Lmodulecheck/parsing/element/McKtDeclaredElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lorg/jetbrains/kotlin/psi/KtProperty;
+ public final fun component3 ()Lmodulecheck/parsing/element/McKtDeclaredElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtProperty;Lmodulecheck/parsing/element/McKtDeclaredElement;)Lmodulecheck/parsing/element/kotlin/McKtMemberPropertyImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtMemberPropertyImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lorg/jetbrains/kotlin/psi/KtProperty;Lmodulecheck/parsing/element/McKtDeclaredElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtMemberPropertyImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getAnnotations ()Lmodulecheck/utils/lazy/LazySet;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public fun getDeclaredName ()Lmodulecheck/parsing/source/DeclaredName;
+ public fun getPackageName ()Lmodulecheck/parsing/source/PackageName;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtDeclaredElement;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtCallableDeclaration;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtProperty;
+ public fun getSimpleNames ()Ljava/util/List;
+ public fun getSimplestName-Jf_0vz4 ()Ljava/lang/String;
+ public fun getTypeReferenceName ()Lmodulecheck/utils/lazy/LazyDeferred;
+ public fun getVisibility ()Lmodulecheck/parsing/element/McVisibility$McKtVisibility;
+ public synthetic fun getVisibility ()Lmodulecheck/parsing/element/McVisibility;
+ public fun hashCode ()I
+ public fun isApi ()Z
+ public fun isMutable ()Z
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class modulecheck/parsing/element/kotlin/McKtObjectImpl : modulecheck/parsing/element/kotlin/AbstractMcKtConcreteType, modulecheck/parsing/element/McType$McConcreteType$McKtConcreteType$McKtObject {
+ public fun (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;Lmodulecheck/parsing/element/McKtElement;)V
+ public final fun component1 ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public final fun component2 ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public final fun component3 ()Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;
+ public final fun component4 ()Lmodulecheck/parsing/element/McKtElement;
+ public final fun copy (Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;Lmodulecheck/parsing/element/McKtElement;)Lmodulecheck/parsing/element/kotlin/McKtObjectImpl;
+ public static synthetic fun copy$default (Lmodulecheck/parsing/element/kotlin/McKtObjectImpl;Lmodulecheck/parsing/element/resolve/McElementContext;Lmodulecheck/parsing/element/McFile$McKtFile;Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;Lmodulecheck/parsing/element/McKtElement;ILjava/lang/Object;)Lmodulecheck/parsing/element/kotlin/McKtObjectImpl;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getContainingFile ()Lmodulecheck/parsing/element/McFile$McKtFile;
+ public synthetic fun getContainingFile ()Lmodulecheck/parsing/element/McFile;
+ public fun getContext ()Lmodulecheck/parsing/element/resolve/McElementContext;
+ public synthetic fun getParent ()Lmodulecheck/parsing/element/McElement;
+ public fun getParent ()Lmodulecheck/parsing/element/McKtElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtClassOrObject;
+ public synthetic fun getPsi ()Lorg/jetbrains/kotlin/psi/KtElement;
+ public fun getPsi ()Lorg/jetbrains/kotlin/psi/KtObjectDeclaration;
+ public fun hashCode ()I
+}
+
diff --git a/modulecheck-parsing/element/impl-kotlin/build.gradle.kts b/modulecheck-parsing/element/impl-kotlin/build.gradle.kts
new file mode 100644
index 0000000000..2704e6ed1c
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/build.gradle.kts
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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.
+ */
+
+plugins {
+ id("mcbuild")
+}
+
+mcbuild {
+ published(artifactId = "modulecheck-parsing-element-kotlin")
+}
+
+dependencies {
+ api(libs.javax.inject)
+ api(libs.kotlin.compiler)
+
+ api(project(path = ":modulecheck-parsing:element:api"))
+ api(project(path = ":modulecheck-parsing:source:api"))
+ api(project(path = ":modulecheck-utils:lazy"))
+
+ compileOnly(libs.kotlin.reflect)
+
+ implementation(project(path = ":modulecheck-name:api"))
+ implementation(project(path = ":modulecheck-parsing:psi"))
+ implementation(project(path = ":modulecheck-utils:stdlib"))
+
+ testImplementation(libs.bundles.junit)
+ testImplementation(libs.bundles.kotest)
+ testImplementation(libs.classgraph)
+
+ testImplementation(project(path = ":modulecheck-api"))
+ testImplementation(project(path = ":modulecheck-internal-testing"))
+ testImplementation(project(path = ":modulecheck-model:dependency:api"))
+ testImplementation(project(path = ":modulecheck-model:sourceset:api"))
+ testImplementation(project(path = ":modulecheck-parsing:element:impl-resolve"))
+ testImplementation(project(path = ":modulecheck-parsing:psi"))
+ testImplementation(project(path = ":modulecheck-parsing:source:testing"))
+ testImplementation(project(path = ":modulecheck-parsing:wiring"))
+ testImplementation(project(path = ":modulecheck-project-generation:api"))
+ testImplementation(project(path = ":modulecheck-project:api"))
+ testImplementation(project(path = ":modulecheck-project:testing"))
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/McElementFactoryImpl.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/McElementFactoryImpl.kt
new file mode 100644
index 0000000000..09683cff5c
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/McElementFactoryImpl.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element
+
+import modulecheck.parsing.element.kotlin.McKtFileImpl
+import modulecheck.parsing.element.resolve.McElementContext
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+
+class McElementFactoryImpl : McElementFactory {
+
+ override suspend fun createKtFile(
+ context: McElementContext,
+ fileSystemFile: File,
+ backingElement: KtFile
+ ): McKtFileImpl = McKtFileImpl(
+ context = context,
+ file = fileSystemFile,
+ psi = context.kotlinEnvironmentDeferred.await().ktFile(fileSystemFile)
+ )
+
+ override fun create(
+ context: McElementContext,
+ fileSystemFile: File,
+ backingElement: T,
+ parent: McElement
+ ): McElement {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/HasMcElementContext.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/HasMcElementContext.kt
new file mode 100644
index 0000000000..70ada78fbd
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/HasMcElementContext.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.parsing.element.resolve.McElementContext
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
+
+interface HasMcElementContext {
+ val context: McElementContext
+
+ suspend fun bindingContext(): BindingContext {
+ return context.bindingContextDeferred.await()
+ }
+
+ suspend fun bindingContext(readOnlySlice: ReadOnlySlice?, key: K): V? {
+ return bindingContext().get(readOnlySlice, key)
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/ImportParser.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/ImportParser.kt
new file mode 100644
index 0000000000..50e9acbe71
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/ImportParser.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.parsing.source.McName.CompatibleLanguage.KOTLIN
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.utils.lazy.unsafeLazy
+import modulecheck.utils.requireNotNull
+import org.jetbrains.kotlin.psi.KtImportDirective
+
+internal class ImportParser(private val importDirectives: List) {
+
+ private val _aliasMap = mutableMapOf()
+
+ val aliasMap: Map by unsafeLazy {
+ imports
+ _aliasMap
+ }
+
+ val imports: Set by lazy {
+
+ importDirectives
+ .asSequence()
+ .filter { it.isValidImport }
+ .filter { !it.isAllUnder }
+ .filter { it.importPath != null }
+ .map { directive ->
+ directive.importPath.requireNotNull()
+ .pathStr
+ .let { ReferenceName(it, KOTLIN) }
+ .apply { maybeCacheAlias(directive) }
+ }
+ .toSet()
+ }
+
+ val wildcards: Set by lazy {
+
+ importDirectives
+ .asSequence()
+ .filter { it.isValidImport }
+ .filter { it.isAllUnder }
+ .filter { it.importPath != null }
+ .map { directive ->
+ directive.importPath.requireNotNull()
+ .pathStr
+ }
+ .toSet()
+ }
+
+ private fun ReferenceName.maybeCacheAlias(importDirective: KtImportDirective) {
+ val explicitReference = this
+
+ // Map an alias to its actual name, so that it can be looked up/inlined while resolving
+ importDirective.alias
+ // The KtImportAlias is `as Foo`. It has three children:
+ // [LeafPsiElement, PsiWhiteSpace, LeafPsiElement], which are [`as`, ` `, `Foo`]
+ // respectively.
+ ?.lastChild
+ ?.text
+ ?.let { alias ->
+ _aliasMap[alias] = explicitReference
+ }
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtAnnotationImpl.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtAnnotationImpl.kt
new file mode 100644
index 0000000000..57c883305a
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtAnnotationImpl.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.parsing.element.McAnnotation.McKtAnnotation
+import modulecheck.parsing.element.McAnnotationArgument.McKtAnnotationArgument
+import modulecheck.parsing.element.McKtElement
+import modulecheck.parsing.element.resolve.McElementContext
+import modulecheck.parsing.element.resolve.NameParser2.NameParser2Packet
+import modulecheck.parsing.psi.kotlinStdLibNameOrNull
+import modulecheck.parsing.source.McName.CompatibleLanguage.KOTLIN
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.parsing.source.ReferenceName.Companion.asReferenceName
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.lazyDeferred
+import modulecheck.utils.mapToSet
+import modulecheck.utils.requireNotNull
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtAnnotated
+import org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.kotlin.psi.KtValueArgument
+
+internal fun KtAnnotated.annotations(
+ context: McElementContext,
+ parent: McKtElement
+): Set = annotationEntries
+ .mapToSet {
+ McKtAnnotationImpl(
+ context = context,
+ psi = it,
+ parent = parent
+ )
+ }
+
+data class McKtAnnotationImpl(
+ private val context: McElementContext,
+ override val psi: KtAnnotationEntry,
+ override val parent: McKtElement
+) : McKtAnnotation {
+
+ override val referenceName = lazyDeferred {
+
+ context.nameParser.parse(
+ NameParser2Packet(
+ file = containingFile,
+ toResolve = psi.shortName!!.asString().asReferenceName(KOTLIN),
+ referenceLanguage = KOTLIN,
+ stdLibNameOrNull = { name.kotlinStdLibNameOrNull() }
+ )
+ )
+ .requireNotNull()
+ }
+}
+
+data class McKtAnnotationArgumentImpl(
+ private val context: McElementContext,
+ override val psi: KtValueArgument,
+ override val parent: McKtElement
+) : McKtAnnotationArgument {
+
+ override val value: Any = TODO()
+
+ override val type: LazyDeferred = lazyDeferred {
+
+ psi
+ TODO()
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtClassImpl.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtClassImpl.kt
new file mode 100644
index 0000000000..48e63d2016
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtClassImpl.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import kotlinx.coroutines.flow.fold
+import kotlinx.coroutines.flow.toSet
+import modulecheck.parsing.element.McAnnotation
+import modulecheck.parsing.element.McFile.McKtFile
+import modulecheck.parsing.element.McFunction.McKtFunction
+import modulecheck.parsing.element.McKtElement
+import modulecheck.parsing.element.McProperty.McKtProperty
+import modulecheck.parsing.element.McType
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType.McKtClass
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType.McKtCompanionObject
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType.McKtInterface
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType.McKtObject
+import modulecheck.parsing.element.McType.McConcreteType.McKtType
+import modulecheck.parsing.element.resolve.McElementContext
+import modulecheck.parsing.source.DeclaredName
+import modulecheck.parsing.source.PackageName
+import modulecheck.parsing.source.SimpleName
+import modulecheck.parsing.source.SimpleName.Companion.stripPackageNameFromFqName
+import modulecheck.utils.lazy.LazySet
+import modulecheck.utils.lazy.lazySet
+import modulecheck.utils.lazy.unsafeLazy
+import modulecheck.utils.mapToSet
+import modulecheck.utils.requireNotNull
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtObjectDeclaration
+
+abstract class AbstractMcKtConcreteType internal constructor(
+ override val context: McElementContext,
+ override val containingFile: McKtFile,
+ override val psi: KtClassOrObject
+) : McKtConcreteType,
+ McKtType,
+ HasMcElementContext {
+ override val simpleNames: List by unsafeLazy {
+ psi.fqName.requireNotNull()
+ .asString()
+ .stripPackageNameFromFqName(containingFile.packageName)
+ }
+ override val declaredName: DeclaredName by lazy {
+ DeclaredName.agnostic(
+ containingFile.packageName,
+ simpleNames
+ )
+ }
+
+ override val innerTypes: LazySet = lazySet {
+ psi.body
+ ?.mcKtConcreteTypesDirect(context, containingFile, parent)
+ .orEmpty()
+ }
+ override val annotations: LazySet = lazySet {
+ psi.annotations(context, this)
+ }
+ override val innerTypesRecursive: LazySet = lazySet {
+ innerTypes.fold(setOf()) { acc, type ->
+ acc + type + type.innerTypesRecursive.toSet()
+ }
+ }
+ override val properties: LazySet = lazySet {
+
+ buildSet {
+
+ for (property in psi.body?.properties.orEmpty()) {
+
+ add(
+ McKtMemberPropertyImpl(
+ context = context,
+ psi = property,
+ parent = this@AbstractMcKtConcreteType
+ )
+ )
+ }
+
+ val valueParams = psi.primaryConstructor?.valueParameters
+ ?.filter { it.hasValOrVar() }
+ .orEmpty()
+
+ for (property in valueParams) {
+ add(
+ McKtConstructorPropertyImpl(
+ context = context,
+ psi = property,
+ parent = this@AbstractMcKtConcreteType
+ )
+ )
+ }
+ }
+ }
+ override val functions: LazySet = lazySet {
+ psi.body?.functions
+ .orEmpty()
+ .mapToSet {
+ McKtFunctionImpl(context = context, psi = it, parent = this)
+ }
+ }
+ override val superTypes: LazySet = lazySet { TODO("Not yet implemented") }
+ override val typeParameters: LazySet = lazySet {
+ TODO("Not yet implemented")
+ }
+ override val packageName: PackageName
+ get() = containingFile.packageName
+
+ final override fun toString(): String {
+ return "${this::class.java.simpleName}(name = `${declaredName.name}`, " +
+ "containingFile=${containingFile.file.path}, " +
+ "psi=${psi::class.simpleName})"
+ }
+}
+
+data class McKtClassImpl(
+ override val context: McElementContext,
+ override val containingFile: McKtFile,
+ override val psi: KtClass,
+ override val parent: McKtElement
+) : AbstractMcKtConcreteType(context, containingFile, psi), McKtClass {
+ override val primaryConstructor: McKtFunction?
+ get() = TODO("Not yet implemented")
+ override val constructors: LazySet
+ get() = TODO("Not yet implemented")
+}
+
+data class McKtInterfaceImpl(
+ override val context: McElementContext,
+ override val containingFile: McKtFile,
+ override val psi: KtClass,
+ override val parent: McKtElement
+) : AbstractMcKtConcreteType(context, containingFile, psi), McKtInterface
+
+data class McKtCompanionObjectImpl(
+ override val context: McElementContext,
+ override val containingFile: McKtFile,
+ override val psi: KtObjectDeclaration,
+ override val parent: McKtElement
+) : AbstractMcKtConcreteType(context, containingFile, psi), McKtCompanionObject
+
+data class McKtObjectImpl(
+ override val context: McElementContext,
+ override val containingFile: McKtFile,
+ override val psi: KtObjectDeclaration,
+ override val parent: McKtElement
+) : AbstractMcKtConcreteType(context, containingFile, psi), McKtObject
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtFileImpl.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtFileImpl.kt
new file mode 100644
index 0000000000..350cdd83c2
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtFileImpl.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.fold
+import kotlinx.coroutines.flow.toSet
+import modulecheck.parsing.element.McAnnotation
+import modulecheck.parsing.element.McFile
+import modulecheck.parsing.element.McFile.McKtFile
+import modulecheck.parsing.element.McFile.McKtFile.ScopeArgumentParseResult
+import modulecheck.parsing.element.McFunction
+import modulecheck.parsing.element.McProperty
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType
+import modulecheck.parsing.element.resolve.McElementContext
+import modulecheck.parsing.source.DeclaredName
+import modulecheck.parsing.source.PackageName
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.utils.lazy.LazySet
+import modulecheck.utils.lazy.dataSource
+import modulecheck.utils.lazy.lazySet
+import modulecheck.utils.lazy.unsafeLazy
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
+import org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+
+class McKtFileImpl(
+ val context: McElementContext,
+ override val file: File,
+ override val psi: KtFile
+) : McKtFile {
+
+ override val annotations: LazySet = lazySet {
+ psi.fileAnnotationList
+ TODO("Not yet implemented")
+ }
+ override val declaredTypes: LazySet = lazySet {
+ psi.mcKtConcreteTypesDirect(
+ context = context,
+ containingFile = this,
+ parent = this
+ )
+ }
+ override val declaredTypesAndInnerTypes: LazySet = lazySet {
+ declaredTypes.fold(setOf()) { acc, type ->
+ acc + type + type.innerTypesRecursive.toSet()
+ }
+ }
+ override val containingFile: McFile.McKtFile get() = this
+
+ override val children = flow {
+ emitAll(declaredTypes)
+ }
+
+ private val fileJavaFacadeName by lazy { psi.javaFileFacadeFqName.asString() }
+
+ private val importParser by unsafeLazy { ImportParser(psi.importDirectives) }
+
+ // For `import com.foo as Bar`, the entry is `"Bar" to "com.foo"`
+ override val importAliases by lazy {
+ importParser.aliasMap
+ }
+
+ override val imports: LazySet.DataSource =
+ dataSource(priority = LazySet.DataSource.Priority.HIGH) {
+ importParser.imports
+ }
+ override val wildcardImports: LazySet.DataSource = dataSource {
+ importParser.wildcards
+ }
+
+ override val packageName: PackageName by lazy { PackageName(psi.packageFqName.asString()) }
+
+ override val topLevelFunctions: LazySet
+ get() = TODO("Not yet implemented")
+ override val topLevelProperties: LazySet
+ get() = TODO("Not yet implemented")
+ override val apiReferences: List> = emptyList()
+ override val references: List> = emptyList()
+ override val declarations: List> = emptyList()
+
+ override suspend fun getAnvilScopeArguments(
+ allAnnotations: List,
+ mergeAnnotations: List
+ ): ScopeArgumentParseResult {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtFunctionImpl.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtFunctionImpl.kt
new file mode 100644
index 0000000000..4c0fb641e0
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtFunctionImpl.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.parsing.element.Declared
+import modulecheck.parsing.element.HasKtVisibility
+import modulecheck.parsing.element.McAnnotation
+import modulecheck.parsing.element.McFunction
+import modulecheck.parsing.element.McKtDeclaredElement
+import modulecheck.parsing.element.McParameter.McKtParameter
+import modulecheck.parsing.element.McProperty.McKtProperty
+import modulecheck.parsing.element.McType
+import modulecheck.parsing.element.resolve.McElementContext
+import modulecheck.parsing.psi.internal.requireReferenceName
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.LazySet
+import modulecheck.utils.lazy.lazyDeferred
+import modulecheck.utils.lazy.lazySet
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtFunction
+import org.jetbrains.kotlin.resolve.BindingContext
+
+/** represents a Kotlin function element. */
+data class McKtFunctionImpl(
+ override val context: McElementContext,
+ override val psi: KtFunction,
+ override val parent: McKtDeclaredElement
+) : McFunction.McKtFunction,
+ HasKtVisibility by VisibilityDelegate(psi),
+ Declared by DeclaredDelegate(psi, parent),
+ HasMcElementContext {
+
+ override val parameters: LazySet = lazySet { TODO("Not yet implemented") }
+ override val properties: LazySet = lazySet { TODO("Not yet implemented") }
+ override val returnType: LazyDeferred = lazyDeferred {
+ bindingContext(BindingContext.FUNCTION, psi)
+ ?.returnType
+ .requireReferenceName()
+ }
+ override val typeParamters: LazySet =
+ lazySet { TODO("Not yet implemented") }
+ override val annotations: LazySet = lazySet { TODO("Not yet implemented") }
+ override val typeParameters: LazySet =
+ lazySet { TODO("Not yet implemented") }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtMemberPropertyImpl.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtMemberPropertyImpl.kt
new file mode 100644
index 0000000000..68320384e4
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/McKtMemberPropertyImpl.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.name.TypeName
+import modulecheck.parsing.element.Declared
+import modulecheck.parsing.element.HasKtVisibility
+import modulecheck.parsing.element.McAnnotation
+import modulecheck.parsing.element.McKtDeclaredElement
+import modulecheck.parsing.element.McProperty
+import modulecheck.parsing.element.resolve.McElementContext
+import modulecheck.parsing.psi.internal.requireTypeName
+import modulecheck.utils.lazy.LazyDeferred
+import modulecheck.utils.lazy.LazySet
+import modulecheck.utils.lazy.lazyDeferred
+import modulecheck.utils.lazy.lazySet
+import modulecheck.utils.requireNotNull
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.resolve.BindingContext
+
+data class McKtMemberPropertyImpl(
+ override val context: McElementContext,
+ override val psi: KtProperty,
+ override val parent: McKtDeclaredElement
+) : McProperty.McKtProperty.KtMemberProperty,
+ HasKtVisibility by VisibilityDelegate(psi),
+ Declared by DeclaredDelegate(psi, parent),
+ HasMcElementContext {
+
+ override val typeReferenceName: LazyDeferred = lazyDeferred {
+ bindingContext(BindingContext.VARIABLE, psi)
+ .requireNotNull()
+ .type
+ .requireTypeName()
+ }
+
+ override val annotations: LazySet = lazySet {
+ psi.annotations(context, parent = this)
+ }
+ override val isMutable: Boolean
+ get() = psi.isVar
+}
+
+data class McKtConstructorPropertyImpl(
+ override val context: McElementContext,
+ override val psi: KtParameter,
+ override val parent: McKtDeclaredElement
+) : McProperty.McKtProperty.KtConstructorProperty,
+ HasKtVisibility by VisibilityDelegate(psi),
+ Declared by DeclaredDelegate(psi, parent),
+ HasMcElementContext {
+
+ override val typeReferenceName: LazyDeferred = lazyDeferred {
+ bindingContext(BindingContext.VALUE_PARAMETER, psi)
+ .requireNotNull()
+ .type
+ .requireTypeName()
+ }
+
+ override val annotations: LazySet = lazySet {
+ psi.annotations(context, parent = this)
+ }
+ override val isMutable: Boolean
+ get() = psi.isMutable
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/VisibilityDelegate.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/VisibilityDelegate.kt
new file mode 100644
index 0000000000..ebf187afe8
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/VisibilityDelegate.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.parsing.element.Declared
+import modulecheck.parsing.element.HasKtVisibility
+import modulecheck.parsing.element.McKtDeclaredElement
+import modulecheck.parsing.element.McVisibility
+import modulecheck.parsing.psi.internal.requireSimpleName
+import modulecheck.parsing.source.DeclaredName
+import modulecheck.parsing.source.HasPackageName
+import modulecheck.parsing.source.HasSimpleNames
+import modulecheck.parsing.source.PackageName
+import modulecheck.parsing.source.SimpleName
+import modulecheck.parsing.source.asDeclaredName
+import modulecheck.utils.lazy.unsafeLazy
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtModifierListOwner
+
+internal class VisibilityDelegate(psi: KtModifierListOwner) : HasKtVisibility {
+ override val visibility: McVisibility.McKtVisibility by lazy {
+ when {
+ psi.hasModifier(KtTokens.PRIVATE_KEYWORD) -> McVisibility.Private
+ psi.hasModifier(KtTokens.INTERNAL_KEYWORD) -> McVisibility.McKtVisibility.Internal
+ psi.hasModifier(KtTokens.PROTECTED_KEYWORD) -> McVisibility.Protected
+ psi.hasModifier(KtTokens.PUBLIC_KEYWORD) -> McVisibility.Public
+ else -> McVisibility.Public
+ }
+ }
+}
+
+internal class DeclaredDelegate(
+ psi: T,
+ parent: E
+) : Declared where E : McKtDeclaredElement,
+ E : HasPackageName,
+ E : HasSimpleNames {
+
+ override val packageName: PackageName by unsafeLazy { parent.packageName }
+ override val simpleNames: List by unsafeLazy {
+ parent.simpleNames + psi.requireSimpleName()
+ }
+ override val declaredName: DeclaredName by unsafeLazy {
+ simpleNames.asDeclaredName(packageName)
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/mcKtConcreteType.kt b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/mcKtConcreteType.kt
new file mode 100644
index 0000000000..b4770edf9f
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/main/kotlin/modulecheck/parsing/element/kotlin/mcKtConcreteType.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import modulecheck.parsing.element.McFile.McKtFile
+import modulecheck.parsing.element.McKtElement
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType
+import modulecheck.parsing.element.resolve.McElementContext
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.psi.KtObjectDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
+
+@Suppress("FunctionNaming")
+internal fun McKtConcreteType(
+ context: McElementContext,
+ containingFile: McKtFile,
+ clazz: KtClassOrObject,
+ parent: McKtElement
+): McKtConcreteType? = when (clazz) {
+ is KtClass ->
+ if (clazz.isInterface()) {
+ McKtInterfaceImpl(
+ context = context,
+ containingFile = containingFile,
+ psi = clazz,
+ parent = parent
+ )
+ } else {
+ McKtClassImpl(
+ context = context,
+ containingFile = containingFile,
+ psi = clazz,
+ parent = parent
+ )
+ }
+
+ is KtObjectDeclaration ->
+ if (clazz.isCompanion()) {
+ McKtCompanionObjectImpl(
+ context = context,
+ containingFile = containingFile,
+ psi = clazz,
+ parent = parent
+ )
+ } else {
+ require(!clazz.isObjectLiteral())
+ McKtObjectImpl(
+ context = context,
+ containingFile = containingFile,
+ psi = clazz,
+ parent = parent
+ )
+ }
+
+ else -> null
+}
+
+internal fun KtElement.mcKtConcreteTypesDirect(
+ context: McElementContext,
+ containingFile: McKtFile,
+ parent: McKtElement
+): Set {
+
+ return getChildrenOfType()
+ .mapNotNull { clazz ->
+
+ McKtConcreteType(
+ context = context,
+ containingFile = containingFile,
+ clazz = clazz,
+ parent = parent
+ )
+ }.toSet()
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/test/kotlin/modulecheck/parsing/element/kotlin/HostEnvironment.kt b/modulecheck-parsing/element/impl-kotlin/src/test/kotlin/modulecheck/parsing/element/kotlin/HostEnvironment.kt
new file mode 100644
index 0000000000..be4b10881b
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/test/kotlin/modulecheck/parsing/element/kotlin/HostEnvironment.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import io.github.classgraph.ClassGraph
+import modulecheck.utils.requireNotNull
+import java.io.File
+
+/** Utility object to provide everything we might discover from the host environment. */
+@PublishedApi
+internal object HostEnvironment {
+ val classpath by lazy {
+ getHostClasspaths()
+ }
+
+ val anvilAnnotations: File by lazy {
+ findInClasspath(group = "com.squareup.anvil", module = "annotations")
+ }
+
+ val javaxInject: File by lazy {
+ findInClasspath(group = "javax.inject", module = "javax.inject")
+ }
+
+ val kotlinStdLibJar: File by lazy {
+ findInClasspath(kotlinDependencyRegex("(kotlin-stdlib|kotlin-runtime)"))
+ }
+
+ val kotlinStdLibCommonJar: File by lazy {
+ findInClasspath(kotlinDependencyRegex("kotlin-stdlib-common"))
+ }
+
+ val kotlinStdLibJdkJar: File by lazy {
+ findInClasspath(kotlinDependencyRegex("kotlin-stdlib-jdk[0-9]+"))
+ }
+
+ private fun kotlinDependencyRegex(prefix: String): Regex {
+ return Regex("$prefix(-[0-9]+\\.[0-9]+(\\.[0-9]+)?)([-0-9a-zA-Z]+)?\\.jar")
+ }
+
+ /** Tries to find a file matching the given [regex] in the host process' classpath. */
+ private fun findInClasspath(regex: Regex): File {
+ return classpath.firstOrNull { classpath ->
+ classpath.name.matches(regex)
+ }
+ .requireNotNull { "could not find classpath file via regex: $regex" }
+ }
+
+ /** Tries to find a .jar file given pieces of its maven coordinates */
+ private fun findInClasspath(
+ group: String? = null,
+ module: String? = null,
+ version: String? = null
+ ): File {
+ require(group != null || module != null || version != null)
+ return classpath.firstOrNull { classpath ->
+
+ val classpathIsLocal = classpath.absolutePath.contains(".m2/repository/")
+
+ val (fileGroup, fileModule, fileVersion) = if (classpathIsLocal) {
+ parseMavenLocalClasspath(classpath)
+ } else {
+ parseGradleCacheClasspath(classpath)
+ }
+
+ if (group != null && group != fileGroup) return@firstOrNull false
+ if (module != null && module != fileModule) return@firstOrNull false
+ version == null || version == fileVersion
+ }
+ .requireNotNull {
+ "could not find classpath file [group: $group, module: $module, version: $version]"
+ }
+ }
+
+ private fun parseMavenLocalClasspath(classpath: File): List {
+ // ~/.m2/repository/com/square/anvil/compiler-utils/1.0.0/compiler-utils-1.0.0.jar
+ return classpath.absolutePath
+ .substringAfter(".m2/repository/")
+ // Groups have their dots replaced with file separators, like "com/squareup/anvil".
+ // Module names use dashes, so they're unchanged.
+ .split(File.separatorChar)
+ // ["com", "square", "anvil", "compiler-utils", "1.0.0", "compiler-1.0.0.jar"]
+ // drop the simple name and extension
+ .dropLast(1)
+ .let { segments ->
+
+ listOf(
+ // everything but the last two segments is the group
+ segments.dropLast(2).joinToString("."),
+ // second-to-last segment is the module
+ segments[segments.lastIndex - 1],
+ // the last segment is the version
+ segments.last()
+ )
+ }
+ }
+
+ @Suppress("MagicNumber")
+ private fun parseGradleCacheClasspath(classpath: File): List {
+ // example of a starting path:
+ // [...]/com.square.anvil/compiler/1.0.0/911d07691411f7cbccf00d177ac41c1af38/compiler-1.0.0.jar
+ return classpath.absolutePath
+ .split(File.separatorChar)
+ // [..., "com.square.anvil", "compiler", "1.0.0", "91...38", "compiler-1.0.0.jar"]
+ .dropLast(2)
+ .takeLast(3)
+ }
+
+ /** Returns the files on the classloader's classpath and module path. */
+ fun getHostClasspaths(): List {
+ val classGraph = ClassGraph()
+ .enableSystemJarsAndModules()
+ .removeTemporaryFilesAfterScan()
+
+ val classpaths = classGraph.classpathFiles
+ val modules = classGraph.modules.mapNotNull { it.locationFile }
+
+ return (classpaths + modules).distinctBy(File::getAbsolutePath)
+ }
+}
diff --git a/modulecheck-parsing/element/impl-kotlin/src/test/kotlin/modulecheck/parsing/element/kotlin/McKtFileImplTest.kt b/modulecheck-parsing/element/impl-kotlin/src/test/kotlin/modulecheck/parsing/element/kotlin/McKtFileImplTest.kt
new file mode 100644
index 0000000000..e1630aaf68
--- /dev/null
+++ b/modulecheck-parsing/element/impl-kotlin/src/test/kotlin/modulecheck/parsing/element/kotlin/McKtFileImplTest.kt
@@ -0,0 +1,999 @@
+/*
+ * Copyright (C) 2021-2023 Rick Busarow
+ * 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 modulecheck.parsing.element.kotlin
+
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.toList
+import modulecheck.api.context.jvmFiles
+import modulecheck.model.dependency.ConfigurationName
+import modulecheck.model.dependency.ProjectPath
+import modulecheck.model.sourceset.SourceSetName
+import modulecheck.name.ClassName
+import modulecheck.parsing.element.McProperty.McKtProperty
+import modulecheck.parsing.element.McType.McConcreteType.McKtConcreteType
+import modulecheck.parsing.element.kotlin.McKtFileImplTest.SubjectBuilder.SubjectParams
+import modulecheck.parsing.element.resolve.ConcatenatingParsingInterceptor2
+import modulecheck.parsing.element.resolve.ImportAliasUnwrappingParsingInterceptor2
+import modulecheck.parsing.element.resolve.McElementContext
+import modulecheck.parsing.element.resolve.ParsingChain2
+import modulecheck.parsing.psi.RealKotlinFile
+import modulecheck.parsing.psi.internal.PsiElementResolver
+import modulecheck.parsing.psi.internal.file
+import modulecheck.parsing.psi.kotlinStdLibNameOrNull
+import modulecheck.parsing.source.McName.CompatibleLanguage.KOTLIN
+import modulecheck.parsing.source.ReferenceName
+import modulecheck.parsing.test.McNameTest
+import modulecheck.parsing.wiring.RealAndroidDataBindingNameProvider
+import modulecheck.parsing.wiring.RealAndroidRNameProvider
+import modulecheck.parsing.wiring.RealDeclarationsProvider
+import modulecheck.project.McProject
+import modulecheck.project.generation.ProjectCollector
+import modulecheck.project.test.ProjectTest
+import modulecheck.project.test.ProjectTestEnvironment
+import modulecheck.testing.HasTestEnvironment
+import modulecheck.testing.SkipInStackTrace
+import modulecheck.testing.asTests
+import modulecheck.testing.testFactory
+import modulecheck.utils.capitalize
+import modulecheck.utils.check
+import modulecheck.utils.joinToStringConcat
+import modulecheck.utils.justifyToFirstLine
+import modulecheck.utils.lazy.unsafeLazy
+import modulecheck.utils.mapToSet
+import modulecheck.utils.remove
+import modulecheck.utils.replaceRegex
+import modulecheck.utils.singletonList
+import modulecheck.utils.splitAndMap
+import modulecheck.utils.splitAndTrim
+import modulecheck.utils.toStringPretty
+import org.intellij.lang.annotations.Language
+import org.junit.jupiter.api.DynamicNode
+import org.junit.jupiter.api.Nested
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestFactory
+import java.util.stream.Stream
+
+@Suppress("RemoveRedundantQualifierName")
+internal class McKtFileImplTest : ProjectTest(), McNameTest {
+
+ override val defaultLanguage = KOTLIN
+
+ val ProjectTestEnvironment.lib1: McProject
+ get() = projectCache.getOrPut(ProjectPath.from(":lib1")) {
+ kotlinProject(":lib1") {
+ addKotlinSource(
+ """
+ package com.lib1
+
+ class Lib1Class
+ """
+ )
+ }
+ }
+ val ProjectTestEnvironment.project: McProject
+ get() = projectCache.getOrPut(ProjectPath.from(":subject")) {
+ kotlinProject(":subject") {
+ addDependency(ConfigurationName.api, lib1)
+ }
+ }
+
+ val McNameTest.JvmFileBuilder.ReferenceBuilder.lib1Class
+ get() = kotlin("com.lib1.Lib1Class")
+
+ private val constructorProperty = SubjectBuilder("constructor property") { params ->
+ //language=kotlin
+ """
+ package com.subject
+
+ ${params.imports.joinToString("\n") { "import $it" }}
+
+ class SubjectClass(
+ val subjectProp ${params.afterProperty}
+ )
+
+ ${params.additionalTypes.justifyToFirstLine()}
+ """
+ }
+
+ private val classBodyProperty = SubjectBuilder("class body property") { params ->
+ //language=kotlin
+ """
+ package com.subject
+
+ ${params.imports.joinToString("\n") { "import $it" }}
+
+ class SubjectClass {
+ val subjectProp ${params.afterProperty}
+ }
+
+ ${params.additionalTypes.justifyToFirstLine()}
+ """
+ }
+
+ private val nestedClassBodyProperty = SubjectBuilder("nested class body property") { params ->
+ //language=kotlin
+ """
+ package com.subject
+
+ ${params.imports.joinToString("\n") { "import $it" }}
+
+ class Outer {
+ class SubjectClass {
+ val subjectProp ${params.afterProperty}
+ }
+ }
+
+ ${params.additionalTypes.justifyToFirstLine()}
+ """
+ }
+
+ private val explicitTypes = mapOf(
+ "no-import stdlib types" to sequenceOf(
+ SubjectParams(
+ afterProperty = ": String",
+ typeAsString = "kotlin.String",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": String = \"Hello World\"",
+ typeAsString = "kotlin.String",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": Int",
+ typeAsString = "kotlin.Int",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": Int = 3",
+ typeAsString = "kotlin.Int",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": List",
+ typeAsString = "kotlin.collections.List",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": List = emptyList()",
+ typeAsString = "kotlin.collections.List",
+ imports = listOf()
+ )
+ )
+ .checkUnique()
+ .importAliases(),
+ "other types" to sequenceOf(
+ SubjectParams(
+ afterProperty = ": kotlin.String",
+ typeAsString = "kotlin.String",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": kotlin.String = \"Hello World\"",
+ typeAsString = "kotlin.String",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": String",
+ typeAsString = "kotlin.String",
+ imports = listOf("kotlin.String")
+ ),
+ SubjectParams(
+ afterProperty = ": String = \"Hello World\"",
+ typeAsString = "kotlin.String",
+ imports = listOf("kotlin.String")
+ ),
+ SubjectParams(
+ afterProperty = ": KotlinString",
+ typeAsString = "kotlin.String",
+ imports = listOf("kotlin.String as KotlinString")
+ ),
+ SubjectParams(
+ afterProperty = ": KotlinString = \"Hello World\"",
+ typeAsString = "kotlin.String",
+ imports = listOf("kotlin.String as KotlinString")
+ ),
+ SubjectParams(
+ afterProperty = ": KotlinString",
+ typeAsString = "com.subject.KotlinString",
+ imports = listOf(),
+ additionalTypes = "\n typealias KotlinString = kotlin.String\n "
+ ),
+ SubjectParams(
+ afterProperty = ": KotlinString = \"Hello World\"",
+ typeAsString = "com.subject.KotlinString",
+ imports = listOf(),
+ additionalTypes = "\n typealias KotlinString = kotlin.String\n "
+ ),
+ SubjectParams(
+ afterProperty = ": kotlin.Int",
+ typeAsString = "kotlin.Int",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": kotlin.Int = 3",
+ typeAsString = "kotlin.Int",
+ imports = listOf()
+ ),
+ SubjectParams(
+ afterProperty = ": Int",
+ typeAsString = "kotlin.Int",
+ imports = listOf("kotlin.Int")
+ ),
+ SubjectParams(
+ afterProperty = ": Int = 3",
+ typeAsString = "kotlin.Int",
+ imports = listOf("kotlin.Int")
+ ),
+ SubjectParams(
+ afterProperty = ": Lib1Class",
+ typeAsString = "com.lib1.Lib1Class",
+ imports = listOf("com.lib1.Lib1Class")
+ ),
+ SubjectParams(
+ afterProperty = ": Lib1Class = Lib1Class()",
+ typeAsString = "com.lib1.Lib1Class",
+ imports = listOf("com.lib1.Lib1Class")
+ ),
+ SubjectParams(
+ afterProperty = ": List",
+ typeAsString = "kotlin.collections.List",
+ imports = listOf("com.lib1.Lib1Class")
+ ),
+ SubjectParams(
+ afterProperty = ": List = emptyList()",
+ typeAsString = "kotlin.collections.List