J2K diagnostic tests: prettify

This commit is contained in:
Alexander Udalov
2016-12-19 18:20:55 +03:00
parent 8c0be58f56
commit 0e11615780
6 changed files with 301 additions and 385 deletions
@@ -17,12 +17,9 @@
package org.jetbrains.kotlin.checkers package org.jetbrains.kotlin.checkers
import com.google.common.base.Predicate import com.google.common.base.Predicate
import com.google.common.collect.Sets
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import junit.framework.TestCase
import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.common.DefaultAnalyzerFacade import org.jetbrains.kotlin.analyzer.common.DefaultAnalyzerFacade
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
@@ -31,12 +28,17 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.container.getService import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.* import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.context.SimpleGlobalContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.context.withProject
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.frontend.java.di.createContainerForTopDownAnalyzerForJvm import org.jetbrains.kotlin.frontend.java.di.createContainerForTopDownAnalyzerForJvm
import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis
@@ -46,12 +48,10 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.platform.JvmBuiltIns import org.jetbrains.kotlin.platform.JvmBuiltIns
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
@@ -63,53 +63,47 @@ import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.DescriptorValidator import org.jetbrains.kotlin.test.util.DescriptorValidator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.* import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE
import org.jetbrains.kotlin.utils.rethrow import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE_ALL
import org.junit.Assert import org.junit.Assert
import java.io.File import java.io.File
import java.util.* import java.util.*
abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() { abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
override fun analyzeAndCheck(testDataFile: File, testFiles: List<BaseDiagnosticsTest.TestFile>) { override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
val groupedByModule = testFiles.groupByTo<TestFile, TestModule, LinkedHashMap<TestModule, List<TestFile>>>( val groupedByModule = files.groupBy(TestFile::module)
LinkedHashMap<TestModule, List<TestFile>>()
) { file -> file.module }
val checkLazyResolveLog = testFiles.any { file -> file.checkLazyLog }
var lazyOperationsLog: LazyOperationsLog? = null var lazyOperationsLog: LazyOperationsLog? = null
val context: GlobalContext
val tracker = ExceptionTracker() val tracker = ExceptionTracker()
if (checkLazyResolveLog) { val storageManager: StorageManager
if (files.any(TestFile::checkLazyLog)) {
lazyOperationsLog = LazyOperationsLog(HASH_SANITIZER) lazyOperationsLog = LazyOperationsLog(HASH_SANITIZER)
context = SimpleGlobalContext( storageManager = LoggingStorageManager(
LoggingStorageManager( LockBasedStorageManager.createWithExceptionHandling(tracker),
LockBasedStorageManager.createWithExceptionHandling(tracker), lazyOperationsLog.addRecordFunction
lazyOperationsLog.addRecordFunction
),
tracker
) )
} }
else { else {
context = SimpleGlobalContext(LockBasedStorageManager.createWithExceptionHandling(tracker), tracker) storageManager = LockBasedStorageManager.createWithExceptionHandling(tracker)
} }
val context = SimpleGlobalContext(storageManager, tracker)
val modules = createModules(groupedByModule, context.storageManager) val modules = createModules(groupedByModule, context.storageManager)
val moduleBindings = java.util.HashMap<TestModule, BindingContext>() val moduleBindings = HashMap<TestModule?, BindingContext>()
for ((testModule, testFilesInModule) in groupedByModule) { for ((testModule, testFilesInModule) in groupedByModule) {
val ktFiles = getKtFiles(testFilesInModule, true)
val jetFiles = getJetFiles(testFilesInModule, true) val oldModule = modules[testModule]!!
val oldModule = modules.get(testModule)
val languageVersionSettings = loadLanguageVersionSettings(testFilesInModule) val languageVersionSettings = loadLanguageVersionSettings(testFilesInModule)
val moduleContext = context.withProject(project).withModule(oldModule) val moduleContext = context.withProject(project).withModule(oldModule)
val separateModules = groupedByModule.size == 1 && groupedByModule.keys.iterator().next() == null val separateModules = groupedByModule.size == 1 && groupedByModule.keys.single() == null
val result = analyzeModuleContents( val result = analyzeModuleContents(
moduleContext, jetFiles, CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(), moduleContext, ktFiles, CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(),
languageVersionSettings, separateModules languageVersionSettings, separateModules
) )
if (oldModule != result.moduleDescriptor) { if (oldModule != result.moduleDescriptor) {
@@ -117,36 +111,37 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
// (its API does not support working with a module created beforehand). // (its API does not support working with a module created beforehand).
// So, we should replace the old (effectively discarded) module with the new one everywhere in dependencies. // So, we should replace the old (effectively discarded) module with the new one everywhere in dependencies.
// TODO: dirty hack, refactor this test so that it doesn't create ModuleDescriptor instances // TODO: dirty hack, refactor this test so that it doesn't create ModuleDescriptor instances
modules.put(testModule, result.moduleDescriptor) modules[testModule] = result.moduleDescriptor as ModuleDescriptorImpl
for (module in modules.values) { for (module in modules.values) {
val it = module.testOnly_AllDependentModules.listIterator() @Suppress("DEPRECATION")
val it = (module.testOnly_AllDependentModules as MutableList).listIterator()
while (it.hasNext()) { while (it.hasNext()) {
if (it.next() == oldModule) { if (it.next() == oldModule) {
it.set(result.moduleDescriptor) it.set(result.moduleDescriptor as ModuleDescriptorImpl)
} }
} }
} }
} }
moduleBindings.put(testModule, result.bindingContext) moduleBindings[testModule] = result.bindingContext
checkAllResolvedCallsAreCompleted(jetFiles, result.bindingContext) checkAllResolvedCallsAreCompleted(ktFiles, result.bindingContext)
} }
// We want to always create a test data file (txt) if it was missing, // We want to always create a test data file (txt) if it was missing,
// but don't want to skip the following checks in case this one fails // but don't want to skip the following checks in case this one fails
var exceptionFromLazyResolveLogValidation: Throwable? = null var exceptionFromLazyResolveLogValidation: Throwable? = null
if (checkLazyResolveLog) { if (lazyOperationsLog != null) {
exceptionFromLazyResolveLogValidation = checkLazyResolveLog(lazyOperationsLog, testDataFile) exceptionFromLazyResolveLogValidation = checkLazyResolveLog(lazyOperationsLog, testDataFile)
} }
else { else {
val lazyLogFile = getLazyLogFile(testDataFile) val lazyLogFile = getLazyLogFile(testDataFile)
TestCase.assertFalse("No lazy log expected, but found: " + lazyLogFile.getAbsolutePath(), lazyLogFile.exists()) assertFalse("No lazy log expected, but found: ${lazyLogFile.absolutePath}", lazyLogFile.exists())
} }
var exceptionFromDescriptorValidation: Throwable? = null var exceptionFromDescriptorValidation: Throwable? = null
try { try {
val expectedFile = File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".txt") val expectedFile = File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".txt")
validateAndCompareDescriptorWithFile(expectedFile, testFiles, modules) validateAndCompareDescriptorWithFile(expectedFile, files, modules)
} }
catch (e: Throwable) { catch (e: Throwable) {
exceptionFromDescriptorValidation = e exceptionFromDescriptorValidation = e
@@ -156,11 +151,11 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
var ok = true var ok = true
val actualText = StringBuilder() val actualText = StringBuilder()
for (testFile in testFiles) { for (testFile in files) {
val module = testFile.module val module = testFile.module
val isCommonModule = modules.get(module).getMultiTargetPlatform() === MultiTargetPlatform.Common val isCommonModule = modules[module]!!.getMultiTargetPlatform() == MultiTargetPlatform.Common
ok = ok and testFile.getActualText( ok = ok and testFile.getActualText(
moduleBindings.get(module), actualText, moduleBindings[module]!!, actualText,
shouldSkipJvmSignatureDiagnostics(groupedByModule) || isCommonModule shouldSkipJvmSignatureDiagnostics(groupedByModule) || isCommonModule
) )
} }
@@ -168,7 +163,7 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
var exceptionFromDynamicCallDescriptorsValidation: Throwable? = null var exceptionFromDynamicCallDescriptorsValidation: Throwable? = null
try { try {
val expectedFile = File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".dynamic.txt") val expectedFile = File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".dynamic.txt")
checkDynamicCallDescriptors(expectedFile, testFiles) checkDynamicCallDescriptors(expectedFile, files)
} }
catch (e: Throwable) { catch (e: Throwable) {
exceptionFromDynamicCallDescriptorsValidation = e exceptionFromDynamicCallDescriptorsValidation = e
@@ -176,41 +171,34 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
KotlinTestUtils.assertEqualsToFile(testDataFile, actualText.toString()) KotlinTestUtils.assertEqualsToFile(testDataFile, actualText.toString())
TestCase.assertTrue("Diagnostics mismatch. See the output above", ok) assertTrue("Diagnostics mismatch. See the output above", ok)
// now we throw a previously found error, if any // now we throw a previously found error, if any
if (exceptionFromDescriptorValidation != null) { exceptionFromDescriptorValidation?.let { throw it }
throw rethrow(exceptionFromDescriptorValidation) exceptionFromLazyResolveLogValidation?.let { throw it }
} exceptionFromDynamicCallDescriptorsValidation?.let { throw it }
if (exceptionFromLazyResolveLogValidation != null) {
throw rethrow(exceptionFromLazyResolveLogValidation)
}
if (exceptionFromDynamicCallDescriptorsValidation != null) {
throw rethrow(exceptionFromDynamicCallDescriptorsValidation)
}
performAdditionalChecksAfterDiagnostics(testDataFile, testFiles, groupedByModule, modules, moduleBindings) performAdditionalChecksAfterDiagnostics(testDataFile, files, groupedByModule, modules, moduleBindings)
} }
protected fun performAdditionalChecksAfterDiagnostics( protected open fun performAdditionalChecksAfterDiagnostics(
testDataFile: File, testDataFile: File,
testFiles: List<BaseDiagnosticsTest.TestFile>, testFiles: List<TestFile>,
moduleFiles: Map<BaseDiagnosticsTest.TestModule, List<BaseDiagnosticsTest.TestFile>>, moduleFiles: Map<TestModule?, List<TestFile>>,
moduleDescriptors: Map<BaseDiagnosticsTest.TestModule, ModuleDescriptorImpl>, moduleDescriptors: Map<TestModule?, ModuleDescriptorImpl>,
moduleBindings: Map<BaseDiagnosticsTest.TestModule, BindingContext> moduleBindings: Map<TestModule?, BindingContext>
) { ) {
// To be overridden by diagnostic-like tests. // To be overridden by diagnostic-like tests.
} }
private fun loadLanguageVersionSettings(module: List<BaseDiagnosticsTest.TestFile>): LanguageVersionSettings? { private fun loadLanguageVersionSettings(module: List<TestFile>): LanguageVersionSettings? {
var result: LanguageVersionSettings? = null var result: LanguageVersionSettings? = null
for (file in module) { for (file in module) {
val current = file.customLanguageVersionSettings val current = file.customLanguageVersionSettings
if (current != null) { if (current != null) {
if (result != null && result != current) { if (result != null && result != current) {
Assert.fail( Assert.fail(
"More than one file in the module has " + BaseDiagnosticsTest.LANGUAGE_DIRECTIVE + " or " + "More than one file in the module has $LANGUAGE_DIRECTIVE or $API_VERSION_DIRECTIVE directive specified. " +
BaseDiagnosticsTest.API_VERSION_DIRECTIVE + " directive specified. " +
"This is not supported. Please move all directives into one file" "This is not supported. Please move all directives into one file"
) )
} }
@@ -221,59 +209,49 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
return result return result
} }
private fun checkDynamicCallDescriptors(expectedFile: File, testFiles: List<BaseDiagnosticsTest.TestFile>) { private fun checkDynamicCallDescriptors(expectedFile: File, testFiles: List<TestFile>) {
val serializer = RecursiveDescriptorComparator(RECURSIVE_ALL) val serializer = RecursiveDescriptorComparator(RECURSIVE_ALL)
val actualText = StringBuilder() val actualText = StringBuilder()
for (testFile in testFiles) { for (testFile in testFiles) {
val dynamicCallDescriptors = testFile.dynamicCallDescriptors for (descriptor in testFile.dynamicCallDescriptors) {
for (descriptor in dynamicCallDescriptors) {
val actualSerialized = serializer.serializeRecursively(descriptor) val actualSerialized = serializer.serializeRecursively(descriptor)
actualText.append(actualSerialized) actualText.append(actualSerialized)
} }
} }
if (actualText.length != 0 || expectedFile.exists()) { if (actualText.isNotEmpty() || expectedFile.exists()) {
KotlinTestUtils.assertEqualsToFile(expectedFile, actualText.toString()) KotlinTestUtils.assertEqualsToFile(expectedFile, actualText.toString())
} }
} }
fun shouldSkipJvmSignatureDiagnostics(groupedByModule: Map<BaseDiagnosticsTest.TestModule, List<BaseDiagnosticsTest.TestFile>>): Boolean { protected open fun shouldSkipJvmSignatureDiagnostics(groupedByModule: Map<TestModule?, List<TestFile>>): Boolean =
return groupedByModule.size > 1 groupedByModule.size > 1
}
private fun checkLazyResolveLog(lazyOperationsLog: LazyOperationsLog, testDataFile: File): Throwable? { private fun checkLazyResolveLog(lazyOperationsLog: LazyOperationsLog, testDataFile: File): Throwable? =
var exceptionFromLazyResolveLogValidation: Throwable? = null try {
try { val expectedFile = getLazyLogFile(testDataFile)
val expectedFile = getLazyLogFile(testDataFile) KotlinTestUtils.assertEqualsToFile(expectedFile, lazyOperationsLog.getText(), HASH_SANITIZER)
null
}
catch (e: Throwable) {
e
}
KotlinTestUtils.assertEqualsToFile( private fun getLazyLogFile(testDataFile: File): File =
expectedFile, File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".lazy.log")
lazyOperationsLog.getText(),
HASH_SANITIZER
)
}
catch (e: Throwable) {
exceptionFromLazyResolveLogValidation = e
}
return exceptionFromLazyResolveLogValidation protected open fun analyzeModuleContents(
}
private fun getLazyLogFile(testDataFile: File): File {
return File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".lazy.log")
}
protected fun analyzeModuleContents(
moduleContext: ModuleContext, moduleContext: ModuleContext,
files: List<KtFile>, files: List<KtFile>,
moduleTrace: BindingTrace, moduleTrace: BindingTrace,
languageVersionSettings: LanguageVersionSettings?, languageVersionSettings: LanguageVersionSettings?,
separateModules: Boolean separateModules: Boolean
): AnalysisResult { ): AnalysisResult {
@Suppress("NAME_SHADOWING")
var files = files var files = files
@Suppress("NAME_SHADOWING")
var languageVersionSettings = languageVersionSettings var languageVersionSettings = languageVersionSettings
val configuration: CompilerConfiguration val configuration: CompilerConfiguration
if (languageVersionSettings != null) { if (languageVersionSettings != null) {
@@ -305,22 +283,21 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
val moduleDescriptor = moduleContext.module as ModuleDescriptorImpl val moduleDescriptor = moduleContext.module as ModuleDescriptorImpl
val platform = moduleDescriptor.getMultiTargetPlatform() val platform = moduleDescriptor.getMultiTargetPlatform()
if (platform === MultiTargetPlatform.Common) { if (platform == MultiTargetPlatform.Common) {
return DefaultAnalyzerFacade.analyzeFiles( return DefaultAnalyzerFacade.analyzeFiles(
files, moduleDescriptor.name, true, files, moduleDescriptor.name, true,
mapOf<ModuleDescriptor.Capability<out Any>, Any>( mapOf(
MultiTargetPlatform.CAPABILITY.to<ModuleDescriptor.Capability<MultiTargetPlatform>, MultiTargetPlatform.Common>(MultiTargetPlatform.Common), MultiTargetPlatform.CAPABILITY to MultiTargetPlatform.Common,
MODULE_FILES.to<ModuleDescriptor.Capability<List<KtFile>>, List<KtFile>>(files) MODULE_FILES to files
) )
) { info, content -> ) { _, _ ->
// TODO // TODO
PackagePartProvider.Empty PackagePartProvider.Empty
} }
} }
else if (platform != null) { else if (platform != null) {
// TODO: analyze with the correct platform, not always JVM // TODO: analyze with the correct platform, not always JVM
files = files.plus<KtFile>(getCommonCodeFilesForPlatformSpecificModule(moduleDescriptor)) files += getCommonCodeFilesForPlatformSpecificModule(moduleDescriptor)
} }
val moduleContentScope = GlobalSearchScope.allScope(moduleContext.project) val moduleContentScope = GlobalSearchScope.allScope(moduleContext.project)
@@ -336,16 +313,14 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
moduleClassResolver moduleClassResolver
) )
container.initJvmBuiltInsForTopDownAnalysis(moduleDescriptor, languageVersionSettings) container.initJvmBuiltInsForTopDownAnalysis(moduleDescriptor, languageVersionSettings)
moduleClassResolver.resolver = container.getService(JavaDescriptorResolver::class.java) moduleClassResolver.resolver = container.get<JavaDescriptorResolver>()
moduleDescriptor.initialize(CompositePackageFragmentProvider(Arrays.asList<PackageFragmentProvider>( moduleDescriptor.initialize(CompositePackageFragmentProvider(listOf(
container.getService(KotlinCodeAnalyzer::class.java).getPackageFragmentProvider(), container.get<KotlinCodeAnalyzer>().packageFragmentProvider,
container.getService(JavaDescriptorResolver::class.java).packageFragmentProvider container.get<JavaDescriptorResolver>().packageFragmentProvider
))) )))
container.getService(LazyTopDownAnalyzer::class.java).analyzeDeclarations( container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
TopDownAnalysisMode.TopLevelDeclarations, files, DataFlowInfo.EMPTY
)
return AnalysisResult.success(moduleTrace.bindingContext, moduleDescriptor) return AnalysisResult.success(moduleTrace.bindingContext, moduleDescriptor)
} }
@@ -353,14 +328,16 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
private fun getCommonCodeFilesForPlatformSpecificModule(moduleDescriptor: ModuleDescriptorImpl): List<KtFile> { private fun getCommonCodeFilesForPlatformSpecificModule(moduleDescriptor: ModuleDescriptorImpl): List<KtFile> {
// We assume that a platform-specific module _implements_ all declarations from common modules which are immediate dependencies. // We assume that a platform-specific module _implements_ all declarations from common modules which are immediate dependencies.
// So we collect all sources from such modules to analyze in the platform-specific module as well // So we collect all sources from such modules to analyze in the platform-specific module as well
@Suppress("DEPRECATION")
val dependencies = moduleDescriptor.testOnly_AllDependentModules val dependencies = moduleDescriptor.testOnly_AllDependentModules
// TODO: diagnostics on common code reported during the platform module analysis should be distinguished somehow // TODO: diagnostics on common code reported during the platform module analysis should be distinguished somehow
// E.g. "<!JVM:IMPLEMENTATION_WITHOUT_HEADER!>...<!> // E.g. "<!JVM:IMPLEMENTATION_WITHOUT_HEADER!>...<!>
val result = ArrayList<KtFile>(0) val result = ArrayList<KtFile>(0)
for (dependency in dependencies) { for (dependency in dependencies) {
if (dependency.getCapability(MultiTargetPlatform.CAPABILITY) === MultiTargetPlatform.Common) { if (dependency.getCapability(MultiTargetPlatform.CAPABILITY) == MultiTargetPlatform.Common) {
val files = dependency.getCapability(MODULE_FILES) ?: error("MODULE_FILES should have been set for the common module: " + dependency) val files = dependency.getCapability(MODULE_FILES)
?: error("MODULE_FILES should have been set for the common module: $dependency")
result.addAll(files) result.addAll(files)
} }
} }
@@ -370,11 +347,11 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
private fun validateAndCompareDescriptorWithFile( private fun validateAndCompareDescriptorWithFile(
expectedFile: File, expectedFile: File,
testFiles: List<BaseDiagnosticsTest.TestFile>, testFiles: List<TestFile>,
modules: Map<BaseDiagnosticsTest.TestModule, ModuleDescriptorImpl> modules: Map<TestModule?, ModuleDescriptorImpl>
) { ) {
if (testFiles.any { file -> InTextDirectivesUtils.isDirectiveDefined(file.expectedText, "// SKIP_TXT") }) { if (testFiles.any { file -> InTextDirectivesUtils.isDirectiveDefined(file.expectedText, "// SKIP_TXT") }) {
TestCase.assertFalse(".txt file should not exist if SKIP_TXT directive is used: " + expectedFile, expectedFile.exists()) assertFalse(".txt file should not exist if SKIP_TXT directive is used: $expectedFile", expectedFile.exists())
return return
} }
@@ -383,14 +360,24 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
val isMultiModuleTest = modules.size != 1 val isMultiModuleTest = modules.size != 1
val rootPackageText = StringBuilder() val rootPackageText = StringBuilder()
val module = modules.keys.sorted().iterator() val sortedModules = modules.keys.sortedWith(Comparator { x, y ->
when {
x == null && y == null -> 0
x == null && y != null -> -1
x != null && y == null -> 1
x != null && y != null -> x.compareTo(y)
else -> error("Unreachable")
}
})
val module = sortedModules.iterator()
while (module.hasNext()) { while (module.hasNext()) {
val moduleDescriptor = modules[module.next()] val moduleDescriptor = modules[module.next()]!!
val aPackage = moduleDescriptor.getPackage(FqName.ROOT) val aPackage = moduleDescriptor.getPackage(FqName.ROOT)
TestCase.assertFalse(aPackage.isEmpty()) assertFalse(aPackage.isEmpty())
if (isMultiModuleTest) { if (isMultiModuleTest) {
rootPackageText.append(String.format("// -- Module: %s --\n", moduleDescriptor.getName())) rootPackageText.append(String.format("// -- Module: %s --\n", moduleDescriptor.name))
} }
val actualSerialized = comparator.serializeRecursively(aPackage) val actualSerialized = comparator.serializeRecursively(aPackage)
@@ -403,7 +390,7 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
val lineCount = StringUtil.getLineBreakCount(rootPackageText) val lineCount = StringUtil.getLineBreakCount(rootPackageText)
assert(lineCount < 1000) { assert(lineCount < 1000) {
"Rendered descriptors of this test take up " + lineCount + " lines. " + "Rendered descriptors of this test take up $lineCount lines. " +
"Please ensure you don't render JRE contents to the .txt file. " + "Please ensure you don't render JRE contents to the .txt file. " +
"Such tests are hard to maintain, take long time to execute and are subject to sudden unreviewed changes anyway." "Such tests are hard to maintain, take long time to execute and are subject to sudden unreviewed changes anyway."
} }
@@ -411,20 +398,19 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
KotlinTestUtils.assertEqualsToFile(expectedFile, rootPackageText.toString()) KotlinTestUtils.assertEqualsToFile(expectedFile, rootPackageText.toString())
} }
private fun createdAffectedPackagesConfiguration(testFiles: List<BaseDiagnosticsTest.TestFile>, modules: Collection<ModuleDescriptor>): RecursiveDescriptorComparator.Configuration { private fun createdAffectedPackagesConfiguration(
val packagesNames = getTopLevelPackagesFromFileList(getJetFiles(testFiles, false)) testFiles: List<TestFile>,
modules: Collection<ModuleDescriptor>
): RecursiveDescriptorComparator.Configuration {
val packagesNames = getTopLevelPackagesFromFileList(getKtFiles(testFiles, false))
val stepIntoFilter = Predicate<DeclarationDescriptor> { descriptor -> val stepIntoFilter = Predicate<DeclarationDescriptor> { descriptor ->
val module = DescriptorUtils.getContainingModuleOrNull(descriptor!!) val module = DescriptorUtils.getContainingModuleOrNull(descriptor!!)
if (!modules.contains(module)) return@Predicate false if (module !in modules) return@Predicate false
if (descriptor is PackageViewDescriptor) { if (descriptor is PackageViewDescriptor) {
val fqName = descriptor.fqName val fqName = descriptor.fqName
return@Predicate fqName.isRoot || fqName.pathSegments().first() in packagesNames
if (fqName.isRoot) return@Predicate true
val firstName = fqName.pathSegments()[0]
return@Predicate packagesNames.contains(firstName)
} }
true true
@@ -433,21 +419,16 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
return RECURSIVE.filterRecursion(stepIntoFilter).withValidationStrategy(DescriptorValidator.ValidationVisitor.errorTypesAllowed()) return RECURSIVE.filterRecursion(stepIntoFilter).withValidationStrategy(DescriptorValidator.ValidationVisitor.errorTypesAllowed())
} }
private fun getTopLevelPackagesFromFileList(files: List<KtFile>): Set<Name> { private fun getTopLevelPackagesFromFileList(files: List<KtFile>): Set<Name> =
val shortNames = LinkedHashSet<Name>() files.mapTo(LinkedHashSet<Name>()) { file ->
for (file in files) { file.packageFqName.pathSegments().firstOrNull() ?: SpecialNames.ROOT_PACKAGE
val packageFqNameSegments = file.packageFqName.pathSegments() }
val name = if (packageFqNameSegments.isEmpty()) SpecialNames.ROOT_PACKAGE else packageFqNameSegments[0]
shortNames.add(name)
}
return shortNames
}
private fun createModules( private fun createModules(
groupedByModule: Map<BaseDiagnosticsTest.TestModule, List<BaseDiagnosticsTest.TestFile>>, groupedByModule: Map<TestModule?, List<TestFile>>,
storageManager: StorageManager storageManager: StorageManager
): Map<BaseDiagnosticsTest.TestModule, ModuleDescriptorImpl> { ): MutableMap<TestModule?, ModuleDescriptorImpl> {
val modules = HashMap<BaseDiagnosticsTest.TestModule, ModuleDescriptorImpl>() val modules = HashMap<TestModule?, ModuleDescriptorImpl>()
for (testModule in groupedByModule.keys) { for (testModule in groupedByModule.keys) {
val module = if (testModule == null) val module = if (testModule == null)
@@ -461,11 +442,11 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
for (testModule in groupedByModule.keys) { for (testModule in groupedByModule.keys) {
if (testModule == null) continue if (testModule == null) continue
val module = modules[testModule] val module = modules[testModule]!!
val dependencies = ArrayList<ModuleDescriptorImpl>() val dependencies = ArrayList<ModuleDescriptorImpl>()
dependencies.add(module) dependencies.add(module)
for (dependency in testModule.getDependencies()) { for (dependency in testModule.getDependencies()) {
dependencies.add(modules[dependency]) dependencies.add(modules[dependency]!!)
} }
dependencies.add(module.builtIns.builtInsModule) dependencies.add(module.builtIns.builtInsModule)
@@ -476,98 +457,76 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
return modules return modules
} }
protected fun getAdditionalDependencies(module: ModuleDescriptorImpl): List<ModuleDescriptorImpl> { protected open fun getAdditionalDependencies(module: ModuleDescriptorImpl): List<ModuleDescriptorImpl> =
return emptyList() emptyList()
}
protected fun createModule(moduleName: String, storageManager: StorageManager): ModuleDescriptorImpl { protected open fun createModule(moduleName: String, storageManager: StorageManager): ModuleDescriptorImpl {
val nameSuffix = moduleName.substringAfterLast("-", "") val nameSuffix = moduleName.substringAfterLast("-", "")
val platform = if (nameSuffix.isEmpty()) val platform =
null if (nameSuffix.isEmpty()) null
else if (nameSuffix == "common") MultiTargetPlatform.Common else MultiTargetPlatform.Specific(nameSuffix) else if (nameSuffix == "common") MultiTargetPlatform.Common else MultiTargetPlatform.Specific(nameSuffix)
val capabilities = if (platform == null) val capabilities: Map<ModuleDescriptor.Capability<*>, Any?> =
emptyMap<Any, Any>() if (platform == null) emptyMap()
else else mapOf(MultiTargetPlatform.CAPABILITY to platform)
Collections.singletonMap<ModuleDescriptor.Capability<MultiTargetPlatform>, MultiTargetPlatform>(MultiTargetPlatform.CAPABILITY, platform)
return ModuleDescriptorImpl( return ModuleDescriptorImpl(
Name.special("<$moduleName>"), storageManager, JvmBuiltIns(storageManager), Name.special("<$moduleName>"), storageManager, JvmBuiltIns(storageManager),
if (platform === MultiTargetPlatform.Common) PlatformKind.DEFAULT else PlatformKind.JVM, if (platform == MultiTargetPlatform.Common) PlatformKind.DEFAULT else PlatformKind.JVM,
SourceKind.TEST, capabilities SourceKind.TEST, capabilities
) )
} }
protected fun createSealedModule(storageManager: StorageManager): ModuleDescriptorImpl { protected open fun createSealedModule(storageManager: StorageManager): ModuleDescriptorImpl =
val moduleDescriptor = createModule("test-module", storageManager) createModule("test-module", storageManager).apply {
moduleDescriptor.setDependencies(moduleDescriptor, moduleDescriptor.builtIns.builtInsModule) setDependencies(this, builtIns.builtInsModule)
return moduleDescriptor
}
private fun checkAllResolvedCallsAreCompleted(jetFiles: List<KtFile>, bindingContext: BindingContext) {
for (file in jetFiles) {
if (!AnalyzingUtils.getSyntaxErrorRanges(file).isEmpty()) {
return
} }
}
private fun checkAllResolvedCallsAreCompleted(ktFiles: List<KtFile>, bindingContext: BindingContext) {
if (ktFiles.any { file -> AnalyzingUtils.getSyntaxErrorRanges(file).isNotEmpty() }) return
val resolvedCallsEntries = bindingContext.getSliceContents(BindingContext.RESOLVED_CALL) val resolvedCallsEntries = bindingContext.getSliceContents(BindingContext.RESOLVED_CALL)
for (entry in resolvedCallsEntries.entries) { for ((call, resolvedCall) in resolvedCallsEntries) {
val element = entry.key.callElement val element = call.callElement
val resolvedCall = entry.value
val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange) val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange)
TestCase.assertTrue("Resolved call for '" + element.text + "'" + lineAndColumn + " is not completed", assertTrue("Resolved call for '${element.text}'$lineAndColumn is not completed",
(resolvedCall as MutableResolvedCall<*>).isCompleted) (resolvedCall as MutableResolvedCall<*>).isCompleted)
} }
checkResolvedCallsInDiagnostics(bindingContext) checkResolvedCallsInDiagnostics(bindingContext)
} }
private fun checkResolvedCallsInDiagnostics(bindingContext: BindingContext) { private fun checkResolvedCallsInDiagnostics(bindingContext: BindingContext) {
val diagnosticsStoringResolvedCalls1 = Sets.newHashSet<DiagnosticFactory1<PsiElement, Collection<ResolvedCall<*>>>>( val diagnosticsStoringResolvedCalls1 = setOf(
OVERLOAD_RESOLUTION_AMBIGUITY, NONE_APPLICABLE, CANNOT_COMPLETE_RESOLVE, UNRESOLVED_REFERENCE_WRONG_RECEIVER, OVERLOAD_RESOLUTION_AMBIGUITY, NONE_APPLICABLE, CANNOT_COMPLETE_RESOLVE, UNRESOLVED_REFERENCE_WRONG_RECEIVER,
ASSIGN_OPERATOR_AMBIGUITY, ITERATOR_AMBIGUITY) ASSIGN_OPERATOR_AMBIGUITY, ITERATOR_AMBIGUITY
val diagnosticsStoringResolvedCalls2 = Sets.newHashSet<DiagnosticFactory2<KtExpression, out Comparable<out Comparable<*>>, Collection<ResolvedCall<*>>>>( )
COMPONENT_FUNCTION_AMBIGUITY, DELEGATE_SPECIAL_FUNCTION_AMBIGUITY, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE) val diagnosticsStoringResolvedCalls2 = setOf(
val diagnostics = bindingContext.diagnostics COMPONENT_FUNCTION_AMBIGUITY, DELEGATE_SPECIAL_FUNCTION_AMBIGUITY, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
for (diagnostic in diagnostics) { )
val factory = diagnostic.factory
if (diagnosticsStoringResolvedCalls1.contains(factory)) { for (diagnostic in bindingContext.diagnostics) {
assertResolvedCallsAreCompleted( when (diagnostic.factory) {
diagnostic, DiagnosticFactory.cast(diagnostic, diagnosticsStoringResolvedCalls1).a) in diagnosticsStoringResolvedCalls1 -> assertResolvedCallsAreCompleted(
} diagnostic, DiagnosticFactory.cast(diagnostic, diagnosticsStoringResolvedCalls1).a
)
if (diagnosticsStoringResolvedCalls2.contains(factory)) { in diagnosticsStoringResolvedCalls2 -> assertResolvedCallsAreCompleted(
assertResolvedCallsAreCompleted( diagnostic, DiagnosticFactory.cast(diagnostic, diagnosticsStoringResolvedCalls2).b
diagnostic, )
DiagnosticFactory.cast<DiagnosticWithParameters2<KtExpression, out Comparable<out Comparable<*>>, Collection<ResolvedCall<*>>>>(diagnostic, diagnosticsStoringResolvedCalls2).b)
} }
} }
} }
private fun assertResolvedCallsAreCompleted( private fun assertResolvedCallsAreCompleted(diagnostic: Diagnostic, resolvedCalls: Collection<ResolvedCall<*>>) {
diagnostic: Diagnostic, resolvedCalls: Collection<ResolvedCall<*>>
) {
var allCallsAreCompleted = true
for (resolvedCall in resolvedCalls) {
if (!(resolvedCall as MutableResolvedCall<*>).isCompleted) {
allCallsAreCompleted = false
}
}
val element = diagnostic.psiElement val element = diagnostic.psiElement
val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange) val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange)
TestCase.assertTrue("Resolved calls stored in " + diagnostic.factory.name + "\n" + assertTrue("Resolved calls stored in ${diagnostic.factory.name}\nfor '${element.text}'$lineAndColumn are not completed",
"for '" + element.text + "'" + lineAndColumn + " are not completed", resolvedCalls.all { (it as MutableResolvedCall<*>).isCompleted })
allCallsAreCompleted)
} }
companion object { companion object {
private val HASH_SANITIZER = fun(s: String): String { private val HASH_SANITIZER = fun(s: String): String = s.replace("@(\\d)+".toRegex(), "")
return s.replace("@(\\d)+".toRegex(), "")
}
private val MODULE_FILES = ModuleDescriptor.Capability<List<KtFile>>("") private val MODULE_FILES = ModuleDescriptor.Capability<List<KtFile>>("")
} }
@@ -16,20 +16,17 @@
package org.jetbrains.kotlin.checkers package org.jetbrains.kotlin.checkers
import com.google.common.collect.ImmutableSet
import com.google.common.collect.Lists
import com.intellij.lang.java.JavaLanguage import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.util.Condition import com.intellij.openapi.util.Condition
import com.intellij.openapi.util.Conditions import com.intellij.openapi.util.Conditions
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory import com.intellij.psi.PsiFileFactory
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.Function
import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.HashMap import org.jetbrains.kotlin.asJava.getJvmSignatureDiagnostics
import org.jetbrains.kotlin.asJava.* import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest.TestFile
import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest.TestModule
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.diagnostics.*
@@ -37,106 +34,84 @@ import org.jetbrains.kotlin.load.java.InternalFlexibleTypeTransformer
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.* import org.jetbrains.kotlin.utils.addIfNotNull
import org.junit.Assert import org.junit.Assert
import java.io.File import java.io.File
import java.util.* import java.util.*
import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnosticsTest.TestModule, BaseDiagnosticsTest.TestFile>() { abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, TestFile>() {
override fun createTestModule(name: String): TestModule =
TestModule(name)
override fun createTestModule(name: String): TestModule { override fun createTestFile(module: TestModule?, fileName: String, text: String, directives: Map<String, String>): TestFile =
return TestModule(name) TestFile(module, fileName, text, directives)
}
override fun createTestFile(module: TestModule, fileName: String, text: String, directives: Map<String, String>): TestFile { override fun doMultiFileTest(
return TestFile(module, fileName, text, directives) file: File,
} modules: @JvmSuppressWildcards Map<String, ModuleAndDependencies>,
testFiles: List<TestFile>
override fun doMultiFileTest(file: File, modules: Map<String, KotlinMultiFileTestWithJava<BaseDiagnosticsTest.TestFile, BaseDiagnosticsTest.TestModule>.ModuleAndDependencies>, testFiles: List<TestFile>) { ) {
for (moduleAndDependencies in modules.values) { for (moduleAndDependencies in modules.values) {
val dependencies = moduleAndDependencies.dependencies.map( moduleAndDependencies.module.getDependencies().addAll(moduleAndDependencies.dependencies.map { name ->
{ name -> modules[name]?.module ?: error("Dependency not found: $name for module ${moduleAndDependencies.module.name}")
val dependency = modules[name] ?: error("Dependency not found: " + })
name +
" for module " +
moduleAndDependencies.module.name)
dependency.module
}
)
moduleAndDependencies.module.getDependencies().addAll(dependencies)
} }
analyzeAndCheck(file, testFiles) analyzeAndCheck(file, testFiles)
} }
protected abstract fun analyzeAndCheck( protected abstract fun analyzeAndCheck(testDataFile: File, files: List<TestFile>)
testDataFile: File,
files: List<TestFile>
)
protected fun getJetFiles(testFiles: List<TestFile>, includeExtras: Boolean): List<KtFile> { protected fun getKtFiles(testFiles: List<TestFile>, includeExtras: Boolean): List<KtFile> {
var declareFlexibleType = false var declareFlexibleType = false
var declareCheckType = false var declareCheckType = false
val jetFiles = Lists.newArrayList<KtFile>() val ktFiles = arrayListOf<KtFile>()
for (testFile in testFiles) { for (testFile in testFiles) {
if (testFile.jetFile != null) { ktFiles.addIfNotNull(testFile.ktFile)
jetFiles.add(testFile.jetFile)
}
declareFlexibleType = declareFlexibleType or testFile.declareFlexibleType declareFlexibleType = declareFlexibleType or testFile.declareFlexibleType
declareCheckType = declareCheckType or testFile.declareCheckType declareCheckType = declareCheckType or testFile.declareCheckType
} }
if (includeExtras) { if (includeExtras) {
if (declareFlexibleType) { if (declareFlexibleType) {
jetFiles.add(KotlinTestUtils.createFile("EXPLICIT_FLEXIBLE_TYPES.kt", EXPLICIT_FLEXIBLE_TYPES_DECLARATIONS, project)) ktFiles.add(KotlinTestUtils.createFile("EXPLICIT_FLEXIBLE_TYPES.kt", EXPLICIT_FLEXIBLE_TYPES_DECLARATIONS, project))
} }
if (declareCheckType) { if (declareCheckType) {
jetFiles.add(KotlinTestUtils.createFile("CHECK_TYPE.kt", CHECK_TYPE_DECLARATIONS, project)) ktFiles.add(KotlinTestUtils.createFile("CHECK_TYPE.kt", CHECK_TYPE_DECLARATIONS, project))
} }
} }
return jetFiles return ktFiles
} }
class TestModule(val name: String) : Comparable<TestModule> { class TestModule(val name: String) : Comparable<TestModule> {
private val dependencies = ArrayList<TestModule>() private val dependencies = ArrayList<TestModule>()
fun getDependencies(): MutableList<TestModule> { fun getDependencies(): MutableList<TestModule> = dependencies
return dependencies
}
override fun compareTo(module: TestModule): Int { override fun compareTo(other: TestModule): Int = name.compareTo(other.name)
return name.compareTo(module.name)
}
override fun toString(): String { override fun toString(): String = name
return name
}
} }
class DiagnosticTestLanguageVersionSettings( class DiagnosticTestLanguageVersionSettings(
private val languageFeatures: Map<LanguageFeature, Boolean>, override val apiVersion: ApiVersion private val languageFeatures: Map<LanguageFeature, Boolean>,
override val apiVersion: ApiVersion
) : LanguageVersionSettings { ) : LanguageVersionSettings {
override fun supportsFeature(feature: LanguageFeature): Boolean =
languageFeatures[feature] ?: LanguageVersionSettingsImpl.DEFAULT.supportsFeature(feature)
override fun supportsFeature(feature: LanguageFeature): Boolean { // TODO provide base language version
val enabled = languageFeatures[feature] override val languageVersion: LanguageVersion
return enabled ?: LanguageVersionSettingsImpl.DEFAULT.supportsFeature(feature)
}
override // TODO provide base language version
val languageVersion: LanguageVersion
get() = throw UnsupportedOperationException("This instance of LanguageVersionSettings should be used for tests only") get() = throw UnsupportedOperationException("This instance of LanguageVersionSettings should be used for tests only")
override fun equals(obj: Any?): Boolean { override fun equals(other: Any?): Boolean =
return obj is DiagnosticTestLanguageVersionSettings && other is DiagnosticTestLanguageVersionSettings && other.languageFeatures == languageFeatures && other.apiVersion == apiVersion
obj.languageFeatures == languageFeatures &&
obj.apiVersion == apiVersion override fun hashCode(): Int =
} 31 * languageFeatures.hashCode() + apiVersion.hashCode()
} }
inner class TestFile( inner class TestFile(
@@ -145,14 +120,14 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
textWithMarkers: String, textWithMarkers: String,
directives: Map<String, String> directives: Map<String, String>
) { ) {
private val diagnosedRanges = Lists.newArrayList<CheckerTestUtil.DiagnosedRange>() private val diagnosedRanges: List<CheckerTestUtil.DiagnosedRange> = ArrayList()
val expectedText: String val expectedText: String
private val clearText: String private val clearText: String
val jetFile: KtFile? val ktFile: KtFile?
private val whatDiagnosticsToConsider: Condition<Diagnostic> private val whatDiagnosticsToConsider: Condition<Diagnostic>
val customLanguageVersionSettings: LanguageVersionSettings val customLanguageVersionSettings: LanguageVersionSettings?
private val declareCheckType: Boolean val declareCheckType: Boolean
private val declareFlexibleType: Boolean val declareFlexibleType: Boolean
val checkLazyLog: Boolean val checkLazyLog: Boolean
private val markDynamicCalls: Boolean private val markDynamicCalls: Boolean
val dynamicCallDescriptors: List<DeclarationDescriptor> = ArrayList() val dynamicCallDescriptors: List<DeclarationDescriptor> = ArrayList()
@@ -160,46 +135,43 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
init { init {
this.whatDiagnosticsToConsider = parseDiagnosticFilterDirective(directives) this.whatDiagnosticsToConsider = parseDiagnosticFilterDirective(directives)
this.customLanguageVersionSettings = parseLanguageVersionSettings(directives) this.customLanguageVersionSettings = parseLanguageVersionSettings(directives)
this.checkLazyLog = directives.containsKey(CHECK_LAZY_LOG_DIRECTIVE) || CHECK_LAZY_LOG_DEFAULT this.checkLazyLog = CHECK_LAZY_LOG_DIRECTIVE in directives || CHECK_LAZY_LOG_DEFAULT
this.declareCheckType = directives.containsKey(CHECK_TYPE_DIRECTIVE) this.declareCheckType = CHECK_TYPE_DIRECTIVE in directives
this.declareFlexibleType = directives.containsKey(EXPLICIT_FLEXIBLE_TYPES_DIRECTIVE) this.declareFlexibleType = EXPLICIT_FLEXIBLE_TYPES_DIRECTIVE in directives
this.markDynamicCalls = directives.containsKey(MARK_DYNAMIC_CALLS_DIRECTIVE) this.markDynamicCalls = MARK_DYNAMIC_CALLS_DIRECTIVE in directives
if (fileName.endsWith(".java")) { if (fileName.endsWith(".java")) {
PsiFileFactory.getInstance(project).createFileFromText(fileName, JavaLanguage.INSTANCE, textWithMarkers) PsiFileFactory.getInstance(project).createFileFromText(fileName, JavaLanguage.INSTANCE, textWithMarkers)
// TODO: check there's not syntax errors // TODO: check there's not syntax errors
this.jetFile = null this.ktFile = null
this.clearText = textWithMarkers this.clearText = textWithMarkers
this.expectedText = this.clearText this.expectedText = this.clearText
} }
else { else {
this.expectedText = textWithMarkers this.expectedText = textWithMarkers
val textWithExtras = addExtras(expectedText) this.clearText = CheckerTestUtil.parseDiagnosedRanges(addExtras(expectedText), diagnosedRanges)
this.clearText = CheckerTestUtil.parseDiagnosedRanges(textWithExtras, diagnosedRanges) this.ktFile = TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, project)
this.jetFile = TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, project)
for (diagnosedRange in diagnosedRanges) { for (diagnosedRange in diagnosedRanges) {
diagnosedRange.file = jetFile diagnosedRange.file = ktFile
} }
} }
} }
private val imports: String private val imports: String
get() { get() = buildString {
var imports = "" // Line separator is "\n" intentionally here (see DocumentImpl.assertValidSeparators)
if (declareCheckType) { if (declareCheckType) {
imports += CHECK_TYPE_IMPORT + "\n" append(CHECK_TYPE_IMPORT + "\n")
} }
if (declareFlexibleType) { if (declareFlexibleType) {
imports += EXPLICIT_FLEXIBLE_TYPES_IMPORT + "\n" append(EXPLICIT_FLEXIBLE_TYPES_IMPORT + "\n")
} }
return imports
} }
private val extras: String private val extras: String
get() = "/*extras*/\n$imports/*extras*/\n\n" get() = "/*extras*/\n$imports/*extras*/\n\n"
private fun addExtras(text: String): String { private fun addExtras(text: String): String =
return addImports(text, extras) addImports(text, extras)
}
private fun stripExtras(actualText: StringBuilder) { private fun stripExtras(actualText: StringBuilder) {
val extras = extras val extras = extras
@@ -210,22 +182,22 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
} }
private fun addImports(text: String, imports: String): String { private fun addImports(text: String, imports: String): String {
var text = text var result = text
val pattern = Pattern.compile("^package [\\.\\w\\d]*\n", Pattern.MULTILINE) val pattern = Pattern.compile("^package [\\.\\w\\d]*\n", Pattern.MULTILINE)
val matcher = pattern.matcher(text) val matcher = pattern.matcher(result)
if (matcher.find()) { if (matcher.find()) {
// add imports after the package directive // add imports after the package directive
text = text.substring(0, matcher.end()) + imports + text.substring(matcher.end()) result = result.substring(0, matcher.end()) + imports + result.substring(matcher.end())
} }
else { else {
// add imports at the beginning // add imports at the beginning
text = imports + text result = imports + result
} }
return text return result
} }
fun getActualText(bindingContext: BindingContext, actualText: StringBuilder, skipJvmSignatureDiagnostics: Boolean): Boolean { fun getActualText(bindingContext: BindingContext, actualText: StringBuilder, skipJvmSignatureDiagnostics: Boolean): Boolean {
if (this.jetFile == null) { if (this.ktFile == null) {
// TODO: check java files too // TODO: check java files too
actualText.append(this.clearText) actualText.append(this.clearText)
return true return true
@@ -238,16 +210,16 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
val ok = booleanArrayOf(true) val ok = booleanArrayOf(true)
val diagnostics = ContainerUtil.filter( val diagnostics = ContainerUtil.filter(
CheckerTestUtil.getDiagnosticsIncludingSyntaxErrors(bindingContext, jetFile, markDynamicCalls, dynamicCallDescriptors).plus( CheckerTestUtil.getDiagnosticsIncludingSyntaxErrors(
jvmSignatureDiagnostics), bindingContext, ktFile, markDynamicCalls, dynamicCallDescriptors
) + jvmSignatureDiagnostics,
whatDiagnosticsToConsider whatDiagnosticsToConsider
) )
val diagnosticToExpectedDiagnostic = ContainerUtil.newHashMap<Diagnostic, CheckerTestUtil.TextDiagnostic>() val diagnosticToExpectedDiagnostic = hashMapOf<Diagnostic, CheckerTestUtil.TextDiagnostic>()
CheckerTestUtil.diagnosticsDiff(diagnosticToExpectedDiagnostic, diagnosedRanges, diagnostics, object : CheckerTestUtil.DiagnosticDiffCallbacks { CheckerTestUtil.diagnosticsDiff(diagnosticToExpectedDiagnostic, diagnosedRanges, diagnostics, object : CheckerTestUtil.DiagnosticDiffCallbacks {
override fun missingDiagnostic(diagnostic: CheckerTestUtil.TextDiagnostic, expectedStart: Int, expectedEnd: Int) { override fun missingDiagnostic(diagnostic: CheckerTestUtil.TextDiagnostic, expectedStart: Int, expectedEnd: Int) {
val message = "Missing " + diagnostic.name + DiagnosticUtils.atLocation(jetFile, TextRange(expectedStart, expectedEnd)) val message = "Missing " + diagnostic.name + DiagnosticUtils.atLocation(ktFile, TextRange(expectedStart, expectedEnd))
System.err.println(message) System.err.println(message)
ok[0] = false ok[0] = false
} }
@@ -258,21 +230,23 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
start: Int, start: Int,
end: Int end: Int
) { ) {
val message = "Parameters of diagnostic not equal at position " val message = "Parameters of diagnostic not equal at position " +
+DiagnosticUtils.atLocation(jetFile, TextRange(start, end)) DiagnosticUtils.atLocation(ktFile, TextRange(start, end)) +
+". Expected: " + expectedDiagnostic.asString() + ", actual: " + actualDiagnostic.asString() ". Expected: ${expectedDiagnostic.asString()}, actual: $actualDiagnostic"
System.err.println(message) System.err.println(message)
ok[0] = false ok[0] = false
} }
override fun unexpectedDiagnostic(diagnostic: CheckerTestUtil.TextDiagnostic, actualStart: Int, actualEnd: Int) { override fun unexpectedDiagnostic(diagnostic: CheckerTestUtil.TextDiagnostic, actualStart: Int, actualEnd: Int) {
val message = "Unexpected " + diagnostic.name + DiagnosticUtils.atLocation(jetFile, TextRange(actualStart, actualEnd)) val message = "Unexpected ${diagnostic.name}${DiagnosticUtils.atLocation(ktFile, TextRange(actualStart, actualEnd))}"
System.err.println(message) System.err.println(message)
ok[0] = false ok[0] = false
} }
}) })
actualText.append(CheckerTestUtil.addDiagnosticMarkersToText(jetFile, diagnostics, diagnosticToExpectedDiagnostic, Function<PsiFile, String> { file -> file.text })) actualText.append(
CheckerTestUtil.addDiagnosticMarkersToText(ktFile, diagnostics, diagnosticToExpectedDiagnostic, { file -> file.text })
)
stripExtras(actualText) stripExtras(actualText)
@@ -281,7 +255,7 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
private fun computeJvmSignatureDiagnostics(bindingContext: BindingContext): Set<Diagnostic> { private fun computeJvmSignatureDiagnostics(bindingContext: BindingContext): Set<Diagnostic> {
val jvmSignatureDiagnostics = HashSet<Diagnostic>() val jvmSignatureDiagnostics = HashSet<Diagnostic>()
val declarations = PsiTreeUtil.findChildrenOfType(jetFile, KtDeclaration::class.java) val declarations = PsiTreeUtil.findChildrenOfType(ktFile, KtDeclaration::class.java)
for (declaration in declarations) { for (declaration in declarations) {
val diagnostics = getJvmSignatureDiagnostics(declaration, bindingContext.diagnostics, val diagnostics = getJvmSignatureDiagnostics(declaration, bindingContext.diagnostics,
GlobalSearchScope.allScope(project)) ?: continue GlobalSearchScope.allScope(project)) ?: continue
@@ -290,16 +264,13 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
return jvmSignatureDiagnostics return jvmSignatureDiagnostics
} }
override fun toString(): String { override fun toString(): String = ktFile!!.name
return jetFile!!.name
}
} }
companion object { companion object {
val DIAGNOSTICS_DIRECTIVE = "DIAGNOSTICS" val DIAGNOSTICS_DIRECTIVE = "DIAGNOSTICS"
val DIAGNOSTICS_PATTERN = Pattern.compile("([\\+\\-!])(\\w+)\\s*") val DIAGNOSTICS_PATTERN: Pattern = Pattern.compile("([\\+\\-!])(\\w+)\\s*")
val DIAGNOSTICS_TO_INCLUDE_ANYWAY: ImmutableSet<DiagnosticFactory<*>> = ImmutableSet.of( val DIAGNOSTICS_TO_INCLUDE_ANYWAY: Set<DiagnosticFactory<*>> = setOf(
Errors.UNRESOLVED_REFERENCE, Errors.UNRESOLVED_REFERENCE,
Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER, Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER,
CheckerTestUtil.SyntaxErrorDiagnosticFactory.INSTANCE, CheckerTestUtil.SyntaxErrorDiagnosticFactory.INSTANCE,
@@ -338,9 +309,10 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
val directives = directiveMap[LANGUAGE_DIRECTIVE] val directives = directiveMap[LANGUAGE_DIRECTIVE]
if (apiVersionString == null && directives == null) return null if (apiVersionString == null && directives == null) return null
val apiVersion = (if (apiVersionString != null) ApiVersion.parse(apiVersionString) else ApiVersion.LATEST) ?: error("Unknown API version: " + apiVersionString!!) val apiVersion = (if (apiVersionString != null) ApiVersion.parse(apiVersionString) else ApiVersion.LATEST)
?: error("Unknown API version: $apiVersionString")
val languageFeatures = if (directives == null) emptyMap<LanguageFeature, Boolean>() else collectLanguageFeatureMap(directives) val languageFeatures = directives?.let(this::collectLanguageFeatureMap).orEmpty()
return DiagnosticTestLanguageVersionSettings(languageFeatures, apiVersion) return DiagnosticTestLanguageVersionSettings(languageFeatures, apiVersion)
} }
@@ -349,8 +321,8 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
val matcher = LANGUAGE_PATTERN.matcher(directives) val matcher = LANGUAGE_PATTERN.matcher(directives)
if (!matcher.find()) { if (!matcher.find()) {
Assert.fail( Assert.fail(
"Wrong syntax in the '// !" + LANGUAGE_DIRECTIVE + ": ...' directive:\n" + "Wrong syntax in the '// !$LANGUAGE_DIRECTIVE: ...' directive:\n" +
"found: '" + directives + "'\n" + "found: '$directives'\n" +
"Must be '([+-]LanguageFeatureName)+'\n" + "Must be '([+-]LanguageFeatureName)+'\n" +
"where '+' means 'enable' and '-' means 'disable'\n" + "where '+' means 'enable' and '-' means 'disable'\n" +
"and language feature names are names of enum entries in LanguageFeature enum class" "and language feature names are names of enum entries in LanguageFeature enum class"
@@ -361,15 +333,12 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
do { do {
val enable = matcher.group(1) == "+" val enable = matcher.group(1) == "+"
val name = matcher.group(2) val name = matcher.group(2)
val feature = LanguageFeature.fromString(name) val feature = LanguageFeature.fromString(name) ?: throw AssertionError(
if (feature == null) { "Language feature not found, please check spelling: $name\n" +
Assert.fail( "Known features:\n ${LanguageFeature.values().joinToString("\n ")}"
"Language feature not found, please check spelling: " + name + "\n" + )
"Known features:\n " + join(Arrays.asList(*LanguageFeature.values()), "\n ")
)
}
if (values.put(feature, enable) != null) { if (values.put(feature, enable) != null) {
Assert.fail("Duplicate entry for the language feature: " + name) Assert.fail("Duplicate entry for the language feature: $name")
} }
} }
while (matcher.find()) while (matcher.find())
@@ -382,55 +351,57 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<BaseDiagnostics
if (directives == null) { if (directives == null) {
// If "!API_VERSION" is present, disable the NEWER_VERSION_IN_SINCE_KOTLIN diagnostic. // If "!API_VERSION" is present, disable the NEWER_VERSION_IN_SINCE_KOTLIN diagnostic.
// Otherwise it would be reported in any non-trivial test on the @SinceKotlin value. // Otherwise it would be reported in any non-trivial test on the @SinceKotlin value.
if (directiveMap.containsKey(API_VERSION_DIRECTIVE)) { if (API_VERSION_DIRECTIVE in directiveMap) {
return Condition { diagnostic -> diagnostic.factory !== Errors.NEWER_VERSION_IN_SINCE_KOTLIN } return Condition { diagnostic -> diagnostic.factory !== Errors.NEWER_VERSION_IN_SINCE_KOTLIN }
} }
return Conditions.alwaysTrue<Diagnostic>() return Conditions.alwaysTrue()
} }
var condition = Conditions.alwaysTrue<Diagnostic>() var condition = Conditions.alwaysTrue<Diagnostic>()
val matcher = DIAGNOSTICS_PATTERN.matcher(directives) val matcher = DIAGNOSTICS_PATTERN.matcher(directives)
if (!matcher.find()) { if (!matcher.find()) {
Assert.fail("Wrong syntax in the '// !" + DIAGNOSTICS_DIRECTIVE + ": ...' directive:\n" + Assert.fail("Wrong syntax in the '// !$DIAGNOSTICS_DIRECTIVE: ...' directive:\n" +
"found: '" + directives + "'\n" + "found: '$directives'\n" +
"Must be '([+-!]DIAGNOSTIC_FACTORY_NAME|ERROR|WARNING|INFO)+'\n" + "Must be '([+-!]DIAGNOSTIC_FACTORY_NAME|ERROR|WARNING|INFO)+'\n" +
"where '+' means 'include'\n" + "where '+' means 'include'\n" +
" '-' means 'exclude'\n" + " '-' means 'exclude'\n" +
" '!' means 'exclude everything but this'\n" + " '!' means 'exclude everything but this'\n" +
"directives are applied in the order of appearance, i.e. !FOO +BAR means include only FOO and BAR") "directives are applied in the order of appearance, i.e. !FOO +BAR means include only FOO and BAR")
} }
var first = true var first = true
do { do {
val operation = matcher.group(1) val operation = matcher.group(1)
val name = matcher.group(2) val name = matcher.group(2)
var newCondition: Condition<Diagnostic> val newCondition: Condition<Diagnostic> =
if (ImmutableSet.of("ERROR", "WARNING", "INFO").contains(name)) { if (name in setOf("ERROR", "WARNING", "INFO")) {
val severity = Severity.valueOf(name) Condition { diagnostic -> diagnostic.severity == Severity.valueOf(name) }
newCondition = Condition<Diagnostic> { diagnostic -> diagnostic.severity == severity } }
} else {
else { Condition { diagnostic -> name == diagnostic.factory.name }
newCondition = Condition<Diagnostic> { diagnostic -> name == diagnostic.factory.name } }
}
if ("!" == operation) { when (operation) {
if (!first) { "!" -> {
Assert.fail("'" + operation + name + "' appears in a position rather than the first one, " + if (!first) {
"which effectively cancels all the previous filters in this directive") Assert.fail("'$operation$name' appears in a position rather than the first one, " +
"which effectively cancels all the previous filters in this directive")
}
condition = newCondition
} }
condition = newCondition "+" -> condition = Conditions.or(condition, newCondition)
} "-" -> condition = Conditions.and(condition, Conditions.not(newCondition))
else if ("+" == operation) {
condition = Conditions.or(condition, newCondition)
}
else if ("-" == operation) {
condition = Conditions.and(condition, Conditions.not(newCondition))
} }
first = false first = false
} }
while (matcher.find()) while (matcher.find())
// We always include UNRESOLVED_REFERENCE and SYNTAX_ERROR because they are too likely to indicate erroneous test data // We always include UNRESOLVED_REFERENCE and SYNTAX_ERROR because they are too likely to indicate erroneous test data
return Conditions.or( return Conditions.or(
condition, condition,
Condition<Diagnostic> { diagnostic -> DIAGNOSTICS_TO_INCLUDE_ANYWAY.contains(diagnostic.factory) }) Condition { diagnostic -> diagnostic.factory in DIAGNOSTICS_TO_INCLUDE_ANYWAY }
)
} }
} }
} }
@@ -23,7 +23,7 @@ import java.lang.reflect.GenericDeclaration
class LoggingStorageManager( class LoggingStorageManager(
private val delegate: StorageManager, private val delegate: StorageManager,
private val callHandler: (lambda: Any, call: LoggingStorageManager.CallData?) -> Unit) : ObservableStorageManager(delegate) { private val callHandler: (lambda: Any, call: LoggingStorageManager.CallData) -> Unit) : ObservableStorageManager(delegate) {
class CallData( class CallData(
val fieldOwner: Any?, val fieldOwner: Any?,
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.checkers
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -29,60 +28,55 @@ import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig import org.jetbrains.kotlin.js.config.LibrarySourcesConfig
import org.jetbrains.kotlin.js.resolve.*
import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.js.resolve.MODULE_KIND
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import java.util.*
import java.util.ArrayList import kotlin.reflect.jvm.javaField
abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractDiagnosticsTest() { abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractDiagnosticsTest() {
protected var config: JsConfig? = null protected lateinit var config: JsConfig
private set private set
@Throws(Exception::class)
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
val configuration = environment.configuration.copy() config = LibrarySourcesConfig(project, environment.configuration.copy().apply {
configuration.put(CommonConfigurationKeys.MODULE_NAME, KotlinTestUtils.TEST_MODULE_NAME) put(CommonConfigurationKeys.MODULE_NAME, KotlinTestUtils.TEST_MODULE_NAME)
configuration.put(JSConfigurationKeys.LIBRARY_FILES, LibrarySourcesConfig.JS_STDLIB) put(JSConfigurationKeys.LIBRARY_FILES, LibrarySourcesConfig.JS_STDLIB)
config = LibrarySourcesConfig(project, configuration) })
} }
@Throws(Exception::class)
override fun tearDown() { override fun tearDown() {
config = null (AbstractDiagnosticsTestWithJsStdLib::config).javaField!!.set(this, null)
super.tearDown() super.tearDown()
} }
override fun getEnvironmentConfigFiles(): List<String> { override fun getEnvironmentConfigFiles(): List<String> = EnvironmentConfigFiles.JS_CONFIG_FILES
return EnvironmentConfigFiles.JS_CONFIG_FILES
}
override fun analyzeModuleContents( override fun analyzeModuleContents(
moduleContext: ModuleContext, moduleContext: ModuleContext,
ktFiles: List<KtFile>, files: List<KtFile>,
moduleTrace: BindingTrace, moduleTrace: BindingTrace,
languageVersionSettings: LanguageVersionSettings?, languageVersionSettings: LanguageVersionSettings?,
separateModules: Boolean separateModules: Boolean
): JsAnalysisResult { ): JsAnalysisResult {
// TODO: support LANGUAGE directive in JS diagnostic tests // TODO: support LANGUAGE directive in JS diagnostic tests
assert(languageVersionSettings == null) { BaseDiagnosticsTest.LANGUAGE_DIRECTIVE + " directive is not supported in JS diagnostic tests" } assert(languageVersionSettings == null) { "$LANGUAGE_DIRECTIVE directive is not supported in JS diagnostic tests" }
moduleTrace.record<ModuleDescriptor, ModuleKind>(MODULE_KIND, moduleContext.module, getModuleKind(ktFiles)) moduleTrace.record<ModuleDescriptor, ModuleKind>(MODULE_KIND, moduleContext.module, getModuleKind(files))
return TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace(ktFiles, moduleTrace, moduleContext, config!!) return TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace(files, moduleTrace, moduleContext, config)
} }
private fun getModuleKind(ktFiles: List<KtFile>): ModuleKind { private fun getModuleKind(ktFiles: List<KtFile>): ModuleKind {
var kind = ModuleKind.PLAIN var kind = ModuleKind.PLAIN
for (file in ktFiles) { for (file in ktFiles) {
val text = file.text val text = file.text
for (line in StringUtil.splitByLines(text)) { for (textLine in StringUtil.splitByLines(text)) {
line = line.trim { it <= ' ' } var line = textLine.trim { it <= ' ' }
if (!line.startsWith("//")) continue if (!line.startsWith("//")) continue
line = line.substring(2).trim { it <= ' ' } line = line.substring(2).trim { it <= ' ' }
val parts = StringUtil.split(line, ":") val parts = StringUtil.split(line, ":")
@@ -96,21 +90,13 @@ abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractDiagnosticsTest() {
return kind return kind
} }
override fun getAdditionalDependencies(module: ModuleDescriptorImpl): List<ModuleDescriptorImpl> { override fun getAdditionalDependencies(module: ModuleDescriptorImpl): List<ModuleDescriptorImpl> =
val dependencies = ArrayList<ModuleDescriptorImpl>() config.moduleDescriptors.map { it.data }
for (moduleDescriptor in config!!.moduleDescriptors) {
dependencies.add(moduleDescriptor.data)
}
return dependencies
}
override fun shouldSkipJvmSignatureDiagnostics(groupedByModule: Map<BaseDiagnosticsTest.TestModule, List<BaseDiagnosticsTest.TestFile>>): Boolean { override fun shouldSkipJvmSignatureDiagnostics(groupedByModule: Map<TestModule?, List<TestFile>>): Boolean = true
return true
}
override fun createModule(moduleName: String, storageManager: StorageManager): ModuleDescriptorImpl { override fun createModule(moduleName: String, storageManager: StorageManager): ModuleDescriptorImpl =
return ModuleDescriptorImpl(Name.special("<$moduleName>"), storageManager, JsPlatform.builtIns) ModuleDescriptorImpl(Name.special("<$moduleName>"), storageManager, JsPlatform.builtIns)
}
override fun createSealedModule(storageManager: StorageManager): ModuleDescriptorImpl { override fun createSealedModule(storageManager: StorageManager): ModuleDescriptorImpl {
val module = createModule("kotlin-js-test-module", storageManager) val module = createModule("kotlin-js-test-module", storageManager)
@@ -28,17 +28,17 @@ import org.jetbrains.kotlin.resolve.BindingTrace
abstract class AbstractDiagnosticsTestWithJsStdLibAndBackendCompilation : AbstractDiagnosticsTestWithJsStdLib() { abstract class AbstractDiagnosticsTestWithJsStdLibAndBackendCompilation : AbstractDiagnosticsTestWithJsStdLib() {
override fun analyzeModuleContents( override fun analyzeModuleContents(
moduleContext: ModuleContext, moduleContext: ModuleContext,
ktFiles: MutableList<KtFile>, files: List<KtFile>,
moduleTrace: BindingTrace, moduleTrace: BindingTrace,
languageVersionSettings: LanguageVersionSettings?, languageVersionSettings: LanguageVersionSettings?,
separateModules: Boolean separateModules: Boolean
): JsAnalysisResult { ): JsAnalysisResult {
val analysisResult = super.analyzeModuleContents(moduleContext, ktFiles, moduleTrace, languageVersionSettings, separateModules) val analysisResult = super.analyzeModuleContents(moduleContext, files, moduleTrace, languageVersionSettings, separateModules)
val diagnostics = analysisResult.bindingTrace.bindingContext.diagnostics val diagnostics = analysisResult.bindingTrace.bindingContext.diagnostics
if (!hasError(diagnostics)) { if (!hasError(diagnostics)) {
val translator = K2JSTranslator(config) val translator = K2JSTranslator(config)
translator.translate(ktFiles, MainCallParameters.noCall(), analysisResult) translator.translate(files, MainCallParameters.noCall(), analysisResult)
} }
return analysisResult return analysisResult
@@ -26,14 +26,14 @@ import java.io.File
abstract class AbstractPsi2IrDiagnosticsTest : AbstractDiagnosticsTest() { abstract class AbstractPsi2IrDiagnosticsTest : AbstractDiagnosticsTest() {
override fun performAdditionalChecksAfterDiagnostics( override fun performAdditionalChecksAfterDiagnostics(
testDataFile: File, testDataFile: File,
testFiles: MutableList<TestFile>, testFiles: List<TestFile>,
moduleFiles: MutableMap<TestModule?, MutableList<TestFile>>, moduleFiles: Map<TestModule?, List<TestFile>>,
moduleDescriptors: MutableMap<TestModule?, ModuleDescriptorImpl>, moduleDescriptors: Map<TestModule?, ModuleDescriptorImpl>,
moduleBindings: MutableMap<TestModule?, BindingContext> moduleBindings: Map<TestModule?, BindingContext>
) { ) {
val actualIrDump = StringBuilder() val actualIrDump = StringBuilder()
for (module in moduleFiles.keys) { for (module in moduleFiles.keys) {
val ktFiles = moduleFiles[module]?.mapNotNull { it.jetFile } ?: continue val ktFiles = moduleFiles[module]?.mapNotNull { it.ktFile } ?: continue
val moduleDescriptor = moduleDescriptors[module] ?: continue val moduleDescriptor = moduleDescriptors[module] ?: continue
val moduleBindingContext = moduleBindings[module] ?: continue val moduleBindingContext = moduleBindings[module] ?: continue
val irModule = AbstractIrGeneratorTestCase.generateIrModule(ktFiles, moduleDescriptor, moduleBindingContext, true) val irModule = AbstractIrGeneratorTestCase.generateIrModule(ktFiles, moduleDescriptor, moduleBindingContext, true)