KT-56369: [cinterop] Throw an error for an unexpected module imports

Module imports require explicit '-fmodules' compiler flag.
If the indexer meets a module import but there is no the flag
then it throws an error.
This commit is contained in:
Konstantin Tskhovrebov
2023-09-11 16:01:23 +02:00
committed by Space Team
parent 8e71488adf
commit 4ad5be35f4
2 changed files with 62 additions and 6 deletions
@@ -1,7 +1,7 @@
package org.jetbrains.kotlin.native.interop.indexer
import clang.*
import kotlinx.cinterop.*
import kotlinx.cinterop.readValue
import java.nio.file.Files
data class ModulesInfo(val topLevelHeaders: List<IncludeInfo>, val ownHeaders: Set<String>, val modules: List<String>)
@@ -9,23 +9,29 @@ data class ModulesInfo(val topLevelHeaders: List<IncludeInfo>, val ownHeaders: S
fun getModulesInfo(compilation: Compilation, modules: List<String>): ModulesInfo {
if (modules.isEmpty()) return ModulesInfo(emptyList(), emptySet(), emptyList())
val areModulesEnabled = compilation.compilerArgs.contains("-fmodules")
withIndex(excludeDeclarationsFromPCH = false) { index ->
ModularCompilation(compilation).use {
val modulesASTFiles = getModulesASTFiles(index, it, modules)
return buildModulesInfo(index, modules, modulesASTFiles)
return buildModulesInfo(index, modules, modulesASTFiles, areModulesEnabled)
}
}
}
data class IncludeInfo(val headerPath: String, val moduleName: String?)
private fun buildModulesInfo(index: CXIndex, modules: List<String>, modulesASTFiles: List<String>): ModulesInfo {
private fun buildModulesInfo(
index: CXIndex,
modules: List<String>,
modulesASTFiles: List<String>,
areModulesEnabled: Boolean
): ModulesInfo {
val ownHeaders = mutableSetOf<String>()
val topLevelHeaders = linkedSetOf<IncludeInfo>()
modulesASTFiles.forEach {
val moduleTranslationUnit = clang_createTranslationUnit(index, it)!!
try {
val modulesHeaders = getModulesHeaders(index, moduleTranslationUnit, modules.toSet(), topLevelHeaders)
val modulesHeaders = getModulesHeaders(index, moduleTranslationUnit, modules.toSet(), topLevelHeaders, areModulesEnabled)
modulesHeaders.mapTo(ownHeaders) { it.canonicalPath }
} finally {
clang_disposeTranslationUnit(moduleTranslationUnit)
@@ -35,7 +41,7 @@ private fun buildModulesInfo(index: CXIndex, modules: List<String>, modulesASTFi
return ModulesInfo(topLevelHeaders.toList(), ownHeaders, modules)
}
internal open class ModularCompilation(compilation: Compilation): Compilation by compilation, Disposable {
internal open class ModularCompilation(compilation: Compilation) : Compilation by compilation, Disposable {
companion object {
private const val moduleCacheFlag = "-fmodules-cache-path="
@@ -91,12 +97,28 @@ private fun getModulesHeaders(
index: CXIndex,
translationUnit: CXTranslationUnit,
modules: Set<String>,
topLevelHeaders: LinkedHashSet<IncludeInfo>
topLevelHeaders: LinkedHashSet<IncludeInfo>,
areModulesEnabled: Boolean
): Set<CXFile> {
val nonModularIncludes = mutableMapOf<CXFile, MutableSet<CXFile>>()
val result = mutableSetOf<CXFile>()
val errors = mutableListOf<Throwable>()
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
override fun importedASTFile(info: CXIdxImportedASTFileInfo) {
val isModuleImport = info.isImplicit == 0
if (isModuleImport && !areModulesEnabled) {
val name = clang_Module_getFullName(info.module).convertAndDispose()
val headerPath = clang_indexLoc_getCXSourceLocation(info.loc.readValue()).getContainingFile()?.canonicalPath
val message = buildString {
appendLine("use of '@import' when modules are disabled")
appendLine("header: '$headerPath'")
appendLine("module name: '$name'")
}
errors.add(Error(message))
}
}
override fun ppIncludedFile(info: CXIdxIncludedFileInfo) {
val file = info.file!!
val includer = clang_indexLoc_getCXSourceLocation(info.hashLoc.readValue()).getContainingFile()
@@ -122,6 +144,7 @@ private fun getModulesHeaders(
}
})
if (errors.isNotEmpty()) throw errors.first()
// There are cases when non-modular includes should also be considered as a part of module. For example:
// 1. Some module maps are broken,
@@ -98,6 +98,39 @@ class ModuleTests : IndexerTests() {
assertEquals(listOf(fooH.absolutePath), modulesInfo.topLevelHeaders.canonicalize())
}
@Test
fun testModuleImport() {
val files = TempFiles("testModuleImport")
val fooH = files.file("Foo.h", "@import Bar;")
files.file("Bar.h", "")
files.file("module.modulemap", """
module Foo {
header "Foo.h"
}
module Bar {
header "Bar.h"
}
""".trimIndent())
//without '-fmodules'
val error = assertFails {
getModulesInfo(compilation("-I${files.directory}"), listOf("Foo"))
}
assertContains(
error.message.orEmpty(),
"""
use of '@import' when modules are disabled
header: '${fooH.absolutePath}'
module name: 'Bar'
""".trimIndent()
)
//with '-fmodules'
val modulesInfo = getModulesInfo(compilation("-I${files.directory}", "-fmodules"), listOf("Foo"))
assertEquals(setOf(fooH.absolutePath), modulesInfo.ownHeaders)
assertEquals(listOf(fooH.absolutePath), modulesInfo.topLevelHeaders.canonicalize())
}
@Test
fun testMissingModule() {
val files = TempFiles("testMissingModule")