Use DefaultAnalyzerFacade for common code in diagnostic tests
Because of incomplete infrastructure, introduce a few dirty hacks to make diagnostic tests analyze the common code via DefaultAnalyzerFacade, and also add common module sources to analysis of platform-specific modules. Also do not render 'platform' declarations from platform-specific modules to the .txt file, since they are very likely to be exact copies of the same declaration in the common module (see RecursiveDescriptorComparator)
This commit is contained in:
+15
-5
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.container.useInstance
|
|||||||
import org.jetbrains.kotlin.context.LazyResolveToken
|
import org.jetbrains.kotlin.context.LazyResolveToken
|
||||||
import org.jetbrains.kotlin.context.ModuleContext
|
import org.jetbrains.kotlin.context.ModuleContext
|
||||||
import org.jetbrains.kotlin.context.ProjectContext
|
import org.jetbrains.kotlin.context.ProjectContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||||
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
|
||||||
@@ -54,21 +55,30 @@ object DefaultAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
|||||||
LanguageVersion.LATEST, ApiVersion.LATEST, setOf(LanguageFeature.MultiPlatformProjects)
|
LanguageVersion.LATEST, ApiVersion.LATEST, setOf(LanguageFeature.MultiPlatformProjects)
|
||||||
)
|
)
|
||||||
|
|
||||||
private class SourceModuleInfo(override val name: Name, private val dependOnOldBuiltIns: Boolean) : ModuleInfo {
|
private class SourceModuleInfo(
|
||||||
|
override val name: Name,
|
||||||
|
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>,
|
||||||
|
private val dependOnOldBuiltIns: Boolean
|
||||||
|
) : ModuleInfo {
|
||||||
override fun dependencies() = listOf(this)
|
override fun dependencies() = listOf(this)
|
||||||
|
|
||||||
override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns =
|
override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns =
|
||||||
if (dependOnOldBuiltIns) ModuleInfo.DependenciesOnBuiltIns.LAST else ModuleInfo.DependenciesOnBuiltIns.NONE
|
if (dependOnOldBuiltIns) ModuleInfo.DependenciesOnBuiltIns.LAST else ModuleInfo.DependenciesOnBuiltIns.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analyzeFiles(files: Collection<KtFile>, moduleName: Name, dependOnBuiltIns: Boolean): AnalysisResult {
|
fun analyzeFiles(
|
||||||
val moduleInfo = SourceModuleInfo(moduleName, dependOnBuiltIns)
|
files: Collection<KtFile>, moduleName: Name, dependOnBuiltIns: Boolean,
|
||||||
|
capabilities: Map<ModuleDescriptor.Capability<*>, Any?> = mapOf(MultiTargetPlatform.CAPABILITY to MultiTargetPlatform.Common),
|
||||||
|
packagePartProviderFactory: (ModuleInfo, ModuleContent) -> PackagePartProvider
|
||||||
|
): AnalysisResult {
|
||||||
|
val moduleInfo = SourceModuleInfo(moduleName, capabilities, dependOnBuiltIns)
|
||||||
val project = files.firstOrNull()?.project ?: throw AssertionError("No files to analyze")
|
val project = files.firstOrNull()?.project ?: throw AssertionError("No files to analyze")
|
||||||
val resolver = setupResolverForProject(
|
val resolver = setupResolverForProject(
|
||||||
"sources for metadata serializer",
|
"sources for metadata serializer",
|
||||||
ProjectContext(project), listOf(moduleInfo),
|
ProjectContext(project), listOf(moduleInfo),
|
||||||
{ ModuleContent(files, GlobalSearchScope.EMPTY_SCOPE) },
|
{ ModuleContent(files, GlobalSearchScope.allScope(project)) },
|
||||||
object : PlatformAnalysisParameters {}
|
object : PlatformAnalysisParameters {},
|
||||||
|
packagePartProviderFactory = packagePartProviderFactory
|
||||||
)
|
)
|
||||||
|
|
||||||
val moduleDescriptor = resolver.descriptorForModule(moduleInfo)
|
val moduleDescriptor = resolver.descriptorForModule(moduleInfo)
|
||||||
|
|||||||
+5
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.name.FqName;
|
|||||||
import org.jetbrains.kotlin.renderer.*;
|
import org.jetbrains.kotlin.renderer.*;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.MemberComparator;
|
import org.jetbrains.kotlin.resolve.MemberComparator;
|
||||||
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope;
|
import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
@@ -204,6 +205,10 @@ public class RecursiveDescriptorComparator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'platform' declarations do not belong to the platform-specific module, even though they participate in the analysis
|
||||||
|
if (descriptor instanceof MemberDescriptor && ((MemberDescriptor) descriptor).isPlatform() &&
|
||||||
|
module.getCapability(MultiTargetPlatform.CAPABILITY) != MultiTargetPlatform.Common.INSTANCE) return false;
|
||||||
|
|
||||||
return module.equals(DescriptorUtils.getContainingModule(descriptor));
|
return module.equals(DescriptorUtils.getContainingModule(descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,18 @@ 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.PsiElement;
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
import com.intellij.psi.search.GlobalSearchScope;
|
||||||
|
import kotlin.TuplesKt;
|
||||||
import kotlin.collections.CollectionsKt;
|
import kotlin.collections.CollectionsKt;
|
||||||
|
import kotlin.collections.MapsKt;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
|
import kotlin.jvm.functions.Function2;
|
||||||
import kotlin.text.StringsKt;
|
import kotlin.text.StringsKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleContent;
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo;
|
||||||
|
import org.jetbrains.kotlin.analyzer.common.DefaultAnalyzerFacade;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport;
|
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
|
||||||
@@ -94,6 +100,8 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final ModuleDescriptor.Capability<List<KtFile>> MODULE_FILES = new ModuleDescriptor.Capability<List<KtFile>>("");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void analyzeAndCheck(File testDataFile, List<TestFile> testFiles) {
|
protected void analyzeAndCheck(File testDataFile, List<TestFile> testFiles) {
|
||||||
Map<TestModule, List<TestFile>> groupedByModule = CollectionsKt.groupByTo(
|
Map<TestModule, List<TestFile>> groupedByModule = CollectionsKt.groupByTo(
|
||||||
@@ -141,21 +149,36 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
|
|||||||
|
|
||||||
List<KtFile> jetFiles = getJetFiles(testFilesInModule, true);
|
List<KtFile> jetFiles = getJetFiles(testFilesInModule, true);
|
||||||
|
|
||||||
ModuleDescriptorImpl module = modules.get(testModule);
|
ModuleDescriptorImpl oldModule = modules.get(testModule);
|
||||||
BindingTrace moduleTrace = new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace();
|
|
||||||
|
|
||||||
moduleBindings.put(testModule, moduleTrace.getBindingContext());
|
|
||||||
|
|
||||||
LanguageVersionSettings languageVersionSettings = loadLanguageVersionSettings(testFilesInModule);
|
LanguageVersionSettings languageVersionSettings = loadLanguageVersionSettings(testFilesInModule);
|
||||||
ModuleContext moduleContext = ContextKt.withModule(ContextKt.withProject(context, getProject()), module);
|
ModuleContext moduleContext = ContextKt.withModule(ContextKt.withProject(context, getProject()), oldModule);
|
||||||
|
|
||||||
boolean separateModules = groupedByModule.size() == 1 && groupedByModule.keySet().iterator().next() == null;
|
boolean separateModules = groupedByModule.size() == 1 && groupedByModule.keySet().iterator().next() == null;
|
||||||
AnalysisResult result = analyzeModuleContents(moduleContext, jetFiles, moduleTrace, languageVersionSettings, separateModules);
|
AnalysisResult result = analyzeModuleContents(
|
||||||
if (separateModules) {
|
moduleContext, jetFiles, new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(),
|
||||||
modules.put(testModule, (ModuleDescriptorImpl) result.getModuleDescriptor());
|
languageVersionSettings, separateModules
|
||||||
|
);
|
||||||
|
ModuleDescriptorImpl newModule = (ModuleDescriptorImpl) result.getModuleDescriptor();
|
||||||
|
if (oldModule != newModule) {
|
||||||
|
// For common modules, we use DefaultAnalyzerFacade who creates ModuleDescriptor instances by itself
|
||||||
|
// (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.
|
||||||
|
// TODO: dirty hack, refactor this test so that it doesn't create ModuleDescriptor instances
|
||||||
|
modules.put(testModule, newModule);
|
||||||
|
for (ModuleDescriptorImpl module : modules.values()) {
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
ListIterator<ModuleDescriptorImpl> it = module.getTestOnly_AllDependentModules().listIterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (it.next() == oldModule) {
|
||||||
|
it.set(newModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAllResolvedCallsAreCompleted(jetFiles, moduleTrace.getBindingContext());
|
moduleBindings.put(testModule, result.getBindingContext());
|
||||||
|
checkAllResolvedCallsAreCompleted(jetFiles, result.getBindingContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
@@ -183,8 +206,12 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
|
|||||||
|
|
||||||
StringBuilder actualText = new StringBuilder();
|
StringBuilder actualText = new StringBuilder();
|
||||||
for (TestFile testFile : testFiles) {
|
for (TestFile testFile : testFiles) {
|
||||||
ok &= testFile.getActualText(moduleBindings.get(testFile.getModule()), actualText,
|
TestModule module = testFile.getModule();
|
||||||
shouldSkipJvmSignatureDiagnostics(groupedByModule));
|
boolean isCommonModule = MultiTargetPlatformKt.getMultiTargetPlatform(modules.get(module)) == MultiTargetPlatform.Common.INSTANCE;
|
||||||
|
ok &= testFile.getActualText(
|
||||||
|
moduleBindings.get(module), actualText,
|
||||||
|
shouldSkipJvmSignatureDiagnostics(groupedByModule) || isCommonModule
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Throwable exceptionFromDynamicCallDescriptorsValidation = null;
|
Throwable exceptionFromDynamicCallDescriptorsValidation = null;
|
||||||
@@ -329,6 +356,31 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModuleDescriptorImpl moduleDescriptor = (ModuleDescriptorImpl) moduleContext.getModule();
|
||||||
|
|
||||||
|
MultiTargetPlatform platform = MultiTargetPlatformKt.getMultiTargetPlatform(moduleDescriptor);
|
||||||
|
if (platform == MultiTargetPlatform.Common.INSTANCE) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return DefaultAnalyzerFacade.INSTANCE.analyzeFiles(
|
||||||
|
files, moduleDescriptor.getName(), true,
|
||||||
|
MapsKt.mapOf(
|
||||||
|
TuplesKt.to(MultiTargetPlatform.CAPABILITY, MultiTargetPlatform.Common.INSTANCE),
|
||||||
|
TuplesKt.to(MODULE_FILES, files)
|
||||||
|
),
|
||||||
|
new Function2<ModuleInfo, ModuleContent, PackagePartProvider>() {
|
||||||
|
@Override
|
||||||
|
public PackagePartProvider invoke(ModuleInfo info, ModuleContent content) {
|
||||||
|
// TODO
|
||||||
|
return PackagePartProvider.Empty.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (platform != null) {
|
||||||
|
// TODO: analyze with the correct platform, not always JVM
|
||||||
|
files = CollectionsKt.plus(files, getCommonCodeFilesForPlatformSpecificModule(moduleDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
GlobalSearchScope moduleContentScope = GlobalSearchScope.allScope(moduleContext.getProject());
|
GlobalSearchScope moduleContentScope = GlobalSearchScope.allScope(moduleContext.getProject());
|
||||||
SingleModuleClassResolver moduleClassResolver = new SingleModuleClassResolver();
|
SingleModuleClassResolver moduleClassResolver = new SingleModuleClassResolver();
|
||||||
ComponentProvider container = InjectionKt.createContainerForTopDownAnalyzerForJvm(
|
ComponentProvider container = InjectionKt.createContainerForTopDownAnalyzerForJvm(
|
||||||
@@ -341,10 +393,9 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
|
|||||||
languageVersionSettings,
|
languageVersionSettings,
|
||||||
moduleClassResolver
|
moduleClassResolver
|
||||||
);
|
);
|
||||||
InjectionKt.initJvmBuiltInsForTopDownAnalysis(container, moduleContext.getModule(), languageVersionSettings);
|
InjectionKt.initJvmBuiltInsForTopDownAnalysis(container, moduleDescriptor, languageVersionSettings);
|
||||||
moduleClassResolver.setResolver(DslKt.getService(container, JavaDescriptorResolver.class));
|
moduleClassResolver.setResolver(DslKt.getService(container, JavaDescriptorResolver.class));
|
||||||
|
|
||||||
ModuleDescriptorImpl moduleDescriptor = (ModuleDescriptorImpl) moduleContext.getModule();
|
|
||||||
moduleDescriptor.initialize(new CompositePackageFragmentProvider(Arrays.asList(
|
moduleDescriptor.initialize(new CompositePackageFragmentProvider(Arrays.asList(
|
||||||
DslKt.getService(container, KotlinCodeAnalyzer.class).getPackageFragmentProvider(),
|
DslKt.getService(container, KotlinCodeAnalyzer.class).getPackageFragmentProvider(),
|
||||||
DslKt.getService(container, JavaDescriptorResolver.class).getPackageFragmentProvider()
|
DslKt.getService(container, JavaDescriptorResolver.class).getPackageFragmentProvider()
|
||||||
@@ -357,6 +408,27 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
|
|||||||
return AnalysisResult.success(moduleTrace.getBindingContext(), moduleDescriptor);
|
return AnalysisResult.success(moduleTrace.getBindingContext(), moduleDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static List<KtFile> getCommonCodeFilesForPlatformSpecificModule(@NotNull ModuleDescriptorImpl moduleDescriptor) {
|
||||||
|
// 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
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
List<ModuleDescriptorImpl> dependencies = moduleDescriptor.getTestOnly_AllDependentModules();
|
||||||
|
|
||||||
|
// TODO: diagnostics on common code reported during the platform module analysis should be distinguished somehow
|
||||||
|
// E.g. "<!JVM:PLATFORM_DEFINITION_WITHOUT_DECLARATION!>...<!>
|
||||||
|
List<KtFile> result = new ArrayList<KtFile>(0);
|
||||||
|
for (ModuleDescriptorImpl dependency : dependencies) {
|
||||||
|
if (dependency.getCapability(MultiTargetPlatform.CAPABILITY) == MultiTargetPlatform.Common.INSTANCE) {
|
||||||
|
List<KtFile> files = dependency.getCapability(MODULE_FILES);
|
||||||
|
assert files != null : "MODULE_FILES should have been set for the common module: " + dependency;
|
||||||
|
result.addAll(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private void validateAndCompareDescriptorWithFile(
|
private void validateAndCompareDescriptorWithFile(
|
||||||
File expectedFile,
|
File expectedFile,
|
||||||
List<TestFile> testFiles,
|
List<TestFile> testFiles,
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
|
|||||||
fqName: FqName -> LazyPackageViewDescriptorImpl(this, fqName, storageManager)
|
fqName: FqName -> LazyPackageViewDescriptorImpl(this, fqName, storageManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("This method is not going to be supported. Please do not use it")
|
||||||
|
val testOnly_AllDependentModules: List<ModuleDescriptorImpl> get() = this.dependencies!!.allDependencies
|
||||||
|
|
||||||
override val allDependentModules: List<ModuleDescriptor>
|
override val allDependentModules: List<ModuleDescriptor>
|
||||||
get() = this.dependencies.sure { "Dependencies of module $id were not set" }.allDependencies.filter { it != this }
|
get() = this.dependencies.sure { "Dependencies of module $id were not set" }.allDependencies.filter { it != this }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user