Implement modules in IDE

IDE:
Rewrite AnalyzerFacade and implementations for JS and JVM to support creating separate analyzers for each module
Introduce ModuleInfo which is an intermediate entity between configuration (tests or idea modules) and ModuleDescriptor
Implement IdeaModuleInfos which represent IDEA modules, sdks and libraries
Add (somewhat thin) test checking their behaviour
Implement getModuleInfo() - utility to obtain IdeaModuleInfo for PsiElement
Drop Project.getLazyResolveSession() - not possible to obtain resolve session for the whole project any more
Adjust JavaResolveExtension accordingly
KotlinSignature Intention/Marker - make sure that analyzed element is cls element (he's not in resolve scope otherwise)

LightClasses:
Create separate package light classes for each module
Java code can only reference light class from the first module among it's dependencies
Duplicate jvm signature is only reported on package declarations inside one module

Injectors:
Receive GlobalSearchScope as paramer for VirtualFileFinder and JavaClassFinder
which allows to narrow analyzer scope

JDR:
Introduce ModuleClassResolver resolves java classes in correct java descriptor resolver (corresponding ModuleDescriptor)
Add test checking that java classes belong to correct module

Debugger:
Provide context to analyze files created by debugger in

Converter:
Postprocessor now needs a context to analyze resulting code in

JetPsiFactory:
Add verification that files created by psi factory are not analyzed without context (that is almost never a good idea)

Other:
Use new API in various tests, utilities, run configuration producers and builtin serializers
Various "TODO: (module refactoring)" which mark the unfinished parts
This commit is contained in:
Pavel V. Talanov
2014-06-10 16:50:35 +04:00
parent 07935c837a
commit db5303c019
82 changed files with 1813 additions and 527 deletions
@@ -0,0 +1,199 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.caches.resolve
import com.intellij.testFramework.ModuleTestCase
import com.intellij.openapi.module.StdModuleTypes
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.DependencyScope
import com.intellij.testFramework.UsefulTestCase
import junit.framework.Assert
import com.intellij.openapi.roots.libraries.LibraryTable
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.command.WriteCommandAction
class IdeaModuleInfoTest : ModuleTestCase() {
fun testSimpleModuleDependency() {
val (a, b) = modules()
b.addDependency(a)
b.source.assertDependenciesEqual(b.source, a.source)
assertDoesntContain(a.source.dependencies(), b.source)
}
fun testCircularDependency() {
val (a, b) = modules()
b.addDependency(a)
a.addDependency(b)
a.source.assertDependenciesEqual(a.source, b.source)
b.source.assertDependenciesEqual(b.source, a.source)
}
fun testExportedDependency() {
val (a, b, c) = modules()
b.addDependency(a, exported = true)
c.addDependency(b)
a.source.assertDependenciesEqual(a.source)
b.source.assertDependenciesEqual(b.source, a.source)
c.source.assertDependenciesEqual(c.source, b.source, a.source)
}
fun testRedundantExportedDependency() {
val (a, b, c) = modules()
b.addDependency(a, exported = true)
c.addDependency(a)
c.addDependency(b)
a.source.assertDependenciesEqual(a.source)
b.source.assertDependenciesEqual(b.source, a.source)
c.source.assertDependenciesEqual(c.source, a.source, b.source)
}
fun testCircularExportedDependency() {
val (a, b, c) = modules()
b.addDependency(a, exported = true)
c.addDependency(b, exported = true)
a.addDependency(c, exported = true)
a.source.assertDependenciesEqual(a.source, c.source, b.source)
b.source.assertDependenciesEqual(b.source, a.source, c.source)
c.source.assertDependenciesEqual(c.source, b.source, a.source)
}
fun testSimpleLibDependency() {
val a = module("a")
val lib = projectLibrary()
a.addDependency(lib)
a.source.assertDependenciesEqual(a.source, lib.classes)
}
fun testCircularExportedDependencyWithLib() {
val (a, b, c) = modules()
val lib = projectLibrary()
a.addDependency(lib)
b.addDependency(a, exported = true)
c.addDependency(b, exported = true)
a.addDependency(c, exported = true)
b.addDependency(lib)
c.addDependency(lib)
a.source.assertDependenciesEqual(a.source, lib.classes, c.source, b.source)
b.source.assertDependenciesEqual(b.source, a.source, c.source, lib.classes)
c.source.assertDependenciesEqual(c.source, b.source, a.source, lib.classes)
}
fun testSeveralModulesExportLibs() {
val (a, b, c) = modules()
val lib1 = projectLibrary("lib1")
val lib2 = projectLibrary("lib2")
a.addDependency(lib1, exported = true)
b.addDependency(lib2, exported = true)
c.addDependency(a)
c.addDependency(b)
c.source.assertDependenciesEqual(c.source, a.source, lib1.classes, b.source, lib2.classes)
}
fun testSeveralModulesExportSameLib() {
val (a, b, c) = modules()
val lib = projectLibrary()
a.addDependency(lib, exported = true)
b.addDependency(lib, exported = true)
c.addDependency(a)
c.addDependency(b)
c.source.assertDependenciesEqual(c.source, a.source, lib.classes, b.source)
}
fun testRuntimeDependency() {
val (a, b) = modules()
b.addDependency(a, dependencyScope = DependencyScope.RUNTIME)
b.addDependency(projectLibrary(), dependencyScope = DependencyScope.RUNTIME)
b.source.assertDependenciesEqual(b.source)
}
fun testProvidedDependency() {
val (a, b) = modules()
val lib = projectLibrary()
b.addDependency(a, dependencyScope = DependencyScope.PROVIDED)
b.addDependency(lib, dependencyScope = DependencyScope.PROVIDED)
b.source.assertDependenciesEqual(b.source, a.source, lib.classes)
}
//NOTE: wrapper classes to reduce boilerplate in test cases
private class ModuleDef(val ideaModule: Module) {
val source = ideaModule.toSourceInfo()
}
private inner class LibraryDef(val ideaLibrary: Library) {
val classes = LibraryInfo(getProject()!!, ideaLibrary)
}
private fun ModuleDef.addDependency(
other: ModuleDef,
dependencyScope: DependencyScope = DependencyScope.COMPILE,
exported: Boolean = false
) = ModuleRootModificationUtil.addDependency(this.ideaModule, other.ideaModule, dependencyScope, exported)
private fun ModuleDef.addDependency(
lib: LibraryDef,
dependencyScope: DependencyScope = DependencyScope.COMPILE,
exported: Boolean = false
) = ModuleRootModificationUtil.addDependency(this.ideaModule, lib.ideaLibrary, dependencyScope, exported)
private fun module(name: String): ModuleDef {
val ideaModule = createModuleFromTestData(createTempDirectory()!!.getAbsolutePath(), name, StdModuleTypes.JAVA, false)!!
return ModuleDef(ideaModule)
}
private fun modules(name1: String = "a", name2: String = "b", name3: String = "c") = Triple(module(name1), module(name2), module(name3))
private fun IdeaModuleInfo.assertDependenciesEqual(vararg dependencies: IdeaModuleInfo) {
Assert.assertEquals(dependencies.toList(), this.dependencies())
}
private fun projectLibrary(name: String = "lib"): LibraryDef {
val libraryTable = ProjectLibraryTable.getInstance(myProject)!!
val library = WriteCommandAction.runWriteCommandAction<Library>(myProject) {
libraryTable.createLibrary(name)
}!!
return LibraryDef(library)
}
}
@@ -25,15 +25,13 @@ import org.jetbrains.jet.lang.resolve.java.PackageClassUtils
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import java.util.Collections
import org.jetbrains.jet.plugin.caches.resolve.JavaResolveExtension
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.resolve.resolveTopLevelClass
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor
import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheService
import org.jetbrains.jet.plugin.project.TargetPlatform
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM
import org.jetbrains.jet.lang.resolve.BindingTraceContext
public class DecompiledTextConsistencyTest : JetLightCodeInsightFixtureTestCase() {
@@ -58,8 +56,16 @@ public class DecompiledTextConsistencyTest : JetLightCodeInsightFixtureTestCase(
}
class ProjectBasedResolverForDecompiler(project: Project) : ResolverForDecompiler {
val module = KotlinCacheService.getInstance(project).getGlobalLazyResolveSession(TargetPlatform.JVM).getModuleDescriptor()
val module: ModuleDescriptor = run {
val module = AnalyzerFacadeForJVM.createJavaModule("<module for resolving stdlib with java top down analysis>")
module.addDependencyOnModule(module)
module.addDependencyOnModule(KotlinBuiltIns.getInstance().getBuiltInsModule())
module.seal()
AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project, listOf(), BindingTraceContext(), { false },
module, null, null
).getModuleDescriptor()
}
override fun resolveTopLevelClass(classFqName: FqName): ClassDescriptor? {
return module.resolveTopLevelClass(classFqName)
}