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:
committed by
Space Team
parent
8e71488adf
commit
4ad5be35f4
+29
-6
@@ -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,
|
||||
|
||||
+33
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user