From bcd93b3ef4ebe066140fd966a24a675d5788632c Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Sat, 20 Feb 2021 21:12:00 +0300 Subject: [PATCH 1/7] CARGO: properly set up features of stdlib dependencies --- .../cargo/project/workspace/CargoWorkspace.kt | 92 ++++++++++--------- .../project/workspace/StandardLibrary.kt | 10 +- .../project/model/CargoStdlibPackagesTest.kt | 47 ++++++++++ 3 files changed, 104 insertions(+), 45 deletions(-) create mode 100644 src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt b/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt index 65915cdf18f..c8037b5dd7b 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt @@ -303,14 +303,19 @@ private class WorkspaceImpl( it.id to (stdPackagesByPackageRoot[it.contentRootUrl]?.id ?: it.id) } val newStdlibCrates = stdlib.crates.map { it.copy(id = pkgIdMapping.getValue(it.id)) } - val newStdlibDependencies = stdlib.dependencies.map { (oldId, dependency) -> + val newStdlibDependencies = stdlib.workspaceData.dependencies.map { (oldId, dependency) -> val newDependencies = dependency.mapToSet { it.copy(id = pkgIdMapping.getValue(it.id)) } pkgIdMapping.getValue(oldId) to newDependencies }.toMap() Pair( otherPackagesData + stdPackagesData.map { it.copy(origin = STDLIB) }, - stdlib.copy(crates = newStdlibCrates, dependencies = newStdlibDependencies) + stdlib.copy( + workspaceData = stdlib.workspaceData.copy( + packages = newStdlibCrates, + dependencies = newStdlibDependencies + ) + ) ) } @@ -341,16 +346,7 @@ private class WorkspaceImpl( pkg.dependencies.addAll(stdlibDependencies.filter { it.name !in explicitDeps && it.pkg.id !in stdInternalDeps }) } else { // `pkg` is a crate from stdlib - val stdCrateDeps = stdlib.dependencies[id].orEmpty().mapNotNull { dep -> - val dependencyPackage = newIdToPackage[dep.id] ?: return@mapNotNull null - val depName = dep.name ?: (dependencyPackage.libTarget?.normName ?: dependencyPackage.normName) - DependencyImpl( - dependencyPackage, - depName, - dep.depKinds, - ) - } - pkg.dependencies.addAll(stdCrateDeps) + pkg.addDependencies(stdlib.workspaceData, newIdToPackage) } } } @@ -434,8 +430,21 @@ private class WorkspaceImpl( pkg.cargoEnabledFeatures.map { PackageFeature(pkg, it) } } for ((feature, state) in inferFeatureState(UserDisabledFeatures.EMPTY)) { - if (feature in enabledByCargo != state.isEnabled) { - error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + if (feature.pkg.origin == STDLIB) { + // All features of stdlib package should be considered as enabled by default. + // If `RsExperiments.FETCH_ACTUAL_STDLIB_METADATA` is enabled, + // the plugin invokes `cargo metadata` for `test` crate of stdlib to get actual info. + // And since stdlib packages are not a single workspace, + // `cargo` considers that all stdlib crates except test one are dependencies and + // as a result, enabled only features required by `test` package (i.e. `pkg.cargoEnabledFeatures` are not expected) + // It doesn't make sense to fix `pkg.cargoEnabledFeatures` so let's just adjust this check + if (!state.isEnabled) { + error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + } + } else { + if (feature in enabledByCargo != state.isEnabled) { + error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + } } } } @@ -495,33 +504,7 @@ private class WorkspaceImpl( // Fill package dependencies run { val idToPackage = result.packages.associateBy { it.id } - idToPackage.forEach { (id, pkg) -> - val pkgDeps = data.dependencies[id].orEmpty() - val pkgRawDeps = data.rawDependencies[id].orEmpty() - pkg.dependencies += pkgDeps.mapNotNull { dep -> - val dependencyPackage = idToPackage[dep.id] ?: return@mapNotNull null - - // There can be multiple appropriate raw dependencies because a dependency can be mentioned - // in `Cargo.toml` in different sections, e.g. [dev-dependencies] and [build-dependencies] - val rawDeps = pkgRawDeps.filter { rawDep -> - rawDep.name == dependencyPackage.name && dep.depKinds.any { - it.kind == CargoWorkspace.DepKind.Unclassified || - it.target == rawDep.target && it.kind.cargoName == rawDep.kind - } - } - - val depName = dep.name ?: (dependencyPackage.libTarget?.normName ?: dependencyPackage.normName) - - DependencyImpl( - dependencyPackage, - depName, - dep.depKinds, - rawDeps.any { it.optional }, - rawDeps.any { it.uses_default_features }, - rawDeps.flatMap { it.features }.toSet() - ) - } - } + idToPackage.forEach { (_, pkg) -> pkg.addDependencies(data, idToPackage) } } return result @@ -579,6 +562,33 @@ private class PackageImpl( override fun toString() = "Package(name='$name', contentRootUrl='$contentRootUrl', outDirUrl='$outDirUrl')" } +private fun PackageImpl.addDependencies(workspaceData: CargoWorkspaceData, packagesMap: Map) { + val pkgDeps = workspaceData.dependencies[id].orEmpty() + val pkgRawDeps = workspaceData.rawDependencies[id].orEmpty() + dependencies += pkgDeps.mapNotNull { dep -> + val dependencyPackage = packagesMap[dep.id] ?: return@mapNotNull null + + // There can be multiple appropriate raw dependencies because a dependency can be mentioned + // in `Cargo.toml` in different sections, e.g. [dev-dependencies] and [build-dependencies] + val rawDeps = pkgRawDeps.filter { rawDep -> + rawDep.name == dependencyPackage.name && dep.depKinds.any { + it.kind == CargoWorkspace.DepKind.Unclassified || + it.target == rawDep.target && it.kind.cargoName == rawDep.kind + } + } + + val depName = dep.name ?: (dependencyPackage.libTarget?.normName ?: dependencyPackage.normName) + + DependencyImpl( + dependencyPackage, + depName, + dep.depKinds, + rawDeps.any { it.optional }, + rawDeps.any { it.uses_default_features }, + rawDeps.flatMap { it.features }.toSet() + ) + } +} private class TargetImpl( override val pkg: PackageImpl, diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt b/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt index 7bfa02078d8..c3db043ac3a 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt @@ -31,11 +31,12 @@ import org.rust.stdext.toPath import java.nio.file.Path data class StandardLibrary( - val crates: List, - val dependencies: Map>, + val workspaceData: CargoWorkspaceData, val isHardcoded: Boolean, val isPartOfCargoProject: Boolean = false ) { + val crates: List get() = workspaceData.packages + companion object { private val LOG: Logger = logger() @@ -135,7 +136,8 @@ data class StandardLibrary( } if (crates.isEmpty()) return null - return StandardLibrary(crates.values.toList(), dependencies, isHardcoded = true) + val data = CargoWorkspaceData(crates.values.toList(), dependencies, emptyMap()) + return StandardLibrary(workspaceData = data, isHardcoded = true) } } } @@ -184,7 +186,7 @@ private class StdlibDataFetcher private constructor( // TODO: introduce PackageOrigin.STDLIB_DEPENDENCY if (it.source == null) it.copy(origin = PackageOrigin.STDLIB) else it } - return StandardLibrary(stdlibPackages, stdlibWorkspaceData.dependencies, isHardcoded = false) + return StandardLibrary(stdlibWorkspaceData.copy(packages = stdlibPackages), isHardcoded = false) } diff --git a/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt b/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt new file mode 100644 index 00000000000..2a393cb86c9 --- /dev/null +++ b/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt @@ -0,0 +1,47 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.cargo.project.model + +import org.rust.MinRustcVersion +import org.rust.cargo.RsWithToolchainTestBase +import org.rust.cargo.project.workspace.CargoWorkspace +import org.rust.cargo.project.workspace.FeatureState +import org.rust.ide.experiments.RsExperiments +import org.rust.openapiext.runWithEnabledFeatures +import org.rust.singleProject +import org.rust.workspaceOrFail + +@MinRustcVersion("1.41.0") +class CargoStdlibPackagesTest : RsWithToolchainTestBase() { + + fun `test stdlib dependency`() { + runWithEnabledFeatures(RsExperiments.FETCH_ACTUAL_STDLIB_METADATA) { + buildProject { + toml("Cargo.toml", """ + [package] + name = "sandbox" + version = "0.1.0" + authors = [] + """) + + dir("src") { + rust("lib.rs", "") + } + } + } + + val cargoProject = project.cargoProjects.singleProject() + val workspace = cargoProject.workspaceOrFail() + val hashbrownPkg = workspace.packages.first { it.name == "hashbrown" } + hashbrownPkg.checkFeature("rustc-dep-of-std", FeatureState.Enabled) + hashbrownPkg.checkFeature("default", FeatureState.Disabled) + } + + private fun CargoWorkspace.Package.checkFeature(featureName: String, expectedState: FeatureState) { + val featureState = featureState.getValue(featureName) + assertEquals("Feature `$featureName` in pakcage `$name` should be in $expectedState", expectedState, featureState) + } +} From 0037eb649e997eaf073a3bc9378a9c0dcb2f4f6e Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Wed, 20 Jan 2021 20:06:32 +0300 Subject: [PATCH 2/7] T: check representation of external libraries node in project view Such tests should ensure that we provide proper dependencies by `RsAdditionalLibraryRootsProvider` --- build.gradle.kts | 7 +- .../org/rust/clion/RsCLionProjectViewTest.kt | 10 ++ .../META-INF/plugin.xml | 0 .../rust/ide/idea/RsIdeaProjectViewTest.kt | 10 ++ .../rustSlowTests/RsProjectViewTestBase.kt | 100 ++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 clion/src/test/kotlin/org/rust/clion/RsCLionProjectViewTest.kt rename clion/src/test/{resurces => resources}/META-INF/plugin.xml (100%) create mode 100644 idea/src/test/kotlin/org/rust/ide/idea/RsIdeaProjectViewTest.kt create mode 100644 src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt diff --git a/build.gradle.kts b/build.gradle.kts index eb09948359a..d2ae505699c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,6 +41,7 @@ val psiViewerPlugin = "PsiViewer:${prop("psiViewerPluginVersion")}" val intelliLangPlugin = "org.intellij.intelliLang" val copyrightPlugin = "com.intellij.copyright" val javaPlugin = "com.intellij.java" +val javaIdePlugin = "com.intellij.java.ide" val javaScriptPlugin = "JavaScript" val clionPlugins = listOf("com.intellij.cidr.base", "com.intellij.clion") val mlCompletionPlugin = "com.intellij.completion.ml.ranking" @@ -395,7 +396,11 @@ project(":") { project(":idea") { intellij { version = ideaVersion - setPlugins(javaPlugin) + setPlugins( + javaPlugin, + // this plugin registers `com.intellij.ide.projectView.impl.ProjectViewPane` for IDEA that we use in tests + javaIdePlugin + ) } dependencies { implementation(project(":")) diff --git a/clion/src/test/kotlin/org/rust/clion/RsCLionProjectViewTest.kt b/clion/src/test/kotlin/org/rust/clion/RsCLionProjectViewTest.kt new file mode 100644 index 00000000000..e29ede65d54 --- /dev/null +++ b/clion/src/test/kotlin/org/rust/clion/RsCLionProjectViewTest.kt @@ -0,0 +1,10 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.clion + +import org.rustSlowTests.RsProjectViewTestBase + +class RsCLionProjectViewTest : RsProjectViewTestBase() diff --git a/clion/src/test/resurces/META-INF/plugin.xml b/clion/src/test/resources/META-INF/plugin.xml similarity index 100% rename from clion/src/test/resurces/META-INF/plugin.xml rename to clion/src/test/resources/META-INF/plugin.xml diff --git a/idea/src/test/kotlin/org/rust/ide/idea/RsIdeaProjectViewTest.kt b/idea/src/test/kotlin/org/rust/ide/idea/RsIdeaProjectViewTest.kt new file mode 100644 index 00000000000..66877c1255b --- /dev/null +++ b/idea/src/test/kotlin/org/rust/ide/idea/RsIdeaProjectViewTest.kt @@ -0,0 +1,10 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.ide.idea + +import org.rustSlowTests.RsProjectViewTestBase + +class RsIdeaProjectViewTest : RsProjectViewTestBase() diff --git a/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt b/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt new file mode 100644 index 00000000000..75cf150ddd3 --- /dev/null +++ b/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt @@ -0,0 +1,100 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rustSlowTests + +import com.intellij.ide.projectView.ProjectView +import com.intellij.ide.projectView.impl.ProjectViewPane +import com.intellij.ide.projectView.impl.nodes.ExternalLibrariesNode +import com.intellij.testFramework.PlatformTestUtil +import com.intellij.testFramework.ProjectViewTestUtil +import org.rust.cargo.RsWithToolchainTestBase +import org.rust.cargo.project.model.cargoProjects +import javax.swing.JTree +import javax.swing.tree.DefaultMutableTreeNode +import javax.swing.tree.TreePath + +abstract class RsProjectViewTestBase : RsWithToolchainTestBase() { + + override fun setUp() { + super.setUp() + ProjectViewTestUtil.setupImpl(project, true) + } + + fun `test external library node`() { + buildProject { + toml("Cargo.toml", """ + [package] + name = "intellij-rust-test" + version = "0.1.0" + authors = [] + """) + dir("src") { + rust("main.rs", "") + } + } + + checkExternalLibrariesNode(""" + -External Libraries + +stdlib $presentableStdlibVersion + """) + } + + fun `test external library node with external dependencies`() { + buildProject { + toml("Cargo.toml", """ + [package] + name = "intellij-rust-test" + version = "0.1.0" + authors = [] + + [dependencies] + code-generation-example = "^0.1.0" + """) + dir("src") { + rust("main.rs", "") + } + } + + checkExternalLibrariesNode(""" + -External Libraries + +code-generation-example 0.1.0 + +stdlib $presentableStdlibVersion + """) + } + + private val presentableStdlibVersion: String + get() = project.cargoProjects.allProjects.first().rustcInfo!!.version!!.semver.parsedVersion + + private fun checkExternalLibrariesNode(expected: String) { + val projectView = ProjectView.getInstance(project) + projectView.changeView(ProjectViewPane.ID) + projectView.refresh() + val tree = projectView.currentProjectViewPane.tree + + val path = getExternalLibraries(tree) + tree.expandPath(path) + PlatformTestUtil.waitWhileBusy(tree) + + assertTreeNodeEqual(tree, path, expected) + } + + private fun getExternalLibraries(tree: JTree): TreePath { + PlatformTestUtil.waitWhileBusy(tree) + val root = tree.model.root + for (index in 0 until tree.model.getChildCount(root)) { + val childNode = tree.model.getChild(root, index) as? DefaultMutableTreeNode ?: continue + if (childNode.userObject is ExternalLibrariesNode) { + return TreePath(arrayOf(root, childNode)) + } + } + error("Failed to find External Library node") + } + + private fun assertTreeNodeEqual(tree: JTree, path: TreePath, expected: String) { + val actual = PlatformTestUtil.print(tree, path, null, false) + assertEquals(expected.trimIndent(), actual) + } +} From b7d8f5575b2eaca9a1bb9008863f6368a7d5c671 Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Wed, 24 Feb 2021 22:30:52 +0300 Subject: [PATCH 3/7] CARGO: introduce PackageOrigin.STDLIB_DEPENDENCY and adjust other code As a result, dependencies tree shows stdlib dependencies as part of stdlib node instead of project dependencies --- .../org/rust/cargo/project/workspace/CargoWorkspace.kt | 2 +- .../org/rust/cargo/project/workspace/PackageOrigin.kt | 9 +++++++-- .../workspace/RsAdditionalLibraryRootsProvider.kt | 2 +- .../org/rust/cargo/project/workspace/StandardLibrary.kt | 4 ++-- .../kotlin/org/rust/ide/docs/RsDocumentationProvider.kt | 5 ++--- src/main/kotlin/org/rust/ide/inspections/import/ui.kt | 2 +- .../kotlin/org/rust/ide/refactoring/RsImportOptimizer.kt | 2 +- src/main/kotlin/org/rust/lang/core/CompilerFeature.kt | 4 +++- .../kotlin/org/rust/lang/core/resolve/NameResolution.kt | 2 +- .../org/rust/lang/utils/evaluation/CfgEvaluator.kt | 2 +- .../rust/cargo/project/model/CargoStdlibPackagesTest.kt | 2 ++ 11 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt b/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt index c8037b5dd7b..0b93f6f02bf 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt @@ -320,7 +320,7 @@ private class WorkspaceImpl( } val stdAll = stdlib.crates.associateBy { it.id } - val stdInternalDeps = stdlib.crates.filter { it.origin == DEPENDENCY }.mapToSet { it.id } + val stdInternalDeps = stdlib.crates.filter { it.origin == STDLIB_DEPENDENCY }.mapToSet { it.id } val result = WorkspaceImpl( manifestPath, diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/PackageOrigin.kt b/src/main/kotlin/org/rust/cargo/project/workspace/PackageOrigin.kt index 26c625803ba..34b7d2e31ea 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/PackageOrigin.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/PackageOrigin.kt @@ -20,7 +20,12 @@ enum class PackageOrigin { WORKSPACE, /** - * Other external dependencies (that are not [WORKSPACE]) + * External dependency of [WORKSPACE] or other [DEPENDENCY] package */ - DEPENDENCY; + DEPENDENCY, + + /** + * External dependency of [STDLIB] or other [STDLIB_DEPENDENCY] package + */ + STDLIB_DEPENDENCY } diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/RsAdditionalLibraryRootsProvider.kt b/src/main/kotlin/org/rust/cargo/project/workspace/RsAdditionalLibraryRootsProvider.kt index 162e6eb240d..a20fd738099 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/RsAdditionalLibraryRootsProvider.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/RsAdditionalLibraryRootsProvider.kt @@ -72,7 +72,7 @@ private val CargoProject.ideaLibraries: Collection val dependencyPackages = mutableListOf() for (pkg in workspace.packages) { when (pkg.origin) { - STDLIB -> stdlibPackages += pkg + STDLIB, STDLIB_DEPENDENCY -> stdlibPackages += pkg DEPENDENCY -> dependencyPackages += pkg WORKSPACE -> Unit }.exhaustive diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt b/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt index c3db043ac3a..31f509d04cf 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt @@ -183,8 +183,8 @@ private class StdlibDataFetcher private constructor( ) val stdlibWorkspaceData = CargoMetadata.clean(stdlibMetadataProject) val stdlibPackages = stdlibWorkspaceData.packages.map { - // TODO: introduce PackageOrigin.STDLIB_DEPENDENCY - if (it.source == null) it.copy(origin = PackageOrigin.STDLIB) else it + val newOrigin = if (it.source == null) PackageOrigin.STDLIB else PackageOrigin.STDLIB_DEPENDENCY + it.copy(origin = newOrigin) } return StandardLibrary(stdlibWorkspaceData.copy(packages = stdlibPackages), isHardcoded = false) } diff --git a/src/main/kotlin/org/rust/ide/docs/RsDocumentationProvider.kt b/src/main/kotlin/org/rust/ide/docs/RsDocumentationProvider.kt index 50c5471d4d8..758a93f9174 100644 --- a/src/main/kotlin/org/rust/ide/docs/RsDocumentationProvider.kt +++ b/src/main/kotlin/org/rust/ide/docs/RsDocumentationProvider.kt @@ -13,8 +13,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapiext.Testmark import com.intellij.openapiext.hitOnFalse import com.intellij.psi.* -import org.rust.cargo.project.workspace.PackageOrigin.DEPENDENCY -import org.rust.cargo.project.workspace.PackageOrigin.STDLIB +import org.rust.cargo.project.workspace.PackageOrigin.* import org.rust.cargo.util.AutoInjectedCrates.STD import org.rust.ide.presentation.presentableQualifiedName import org.rust.ide.presentation.presentationInfo @@ -169,7 +168,7 @@ class RsDocumentationProvider : AbstractDocumentationProvider() { val pagePrefix = when (origin) { STDLIB -> STD_DOC_HOST - DEPENDENCY -> { + DEPENDENCY, STDLIB_DEPENDENCY -> { val pkg = (element as? RsElement)?.containingCargoPackage ?: return emptyList() // Packages without source don't have documentation at docs.rs if (pkg.source == null) { diff --git a/src/main/kotlin/org/rust/ide/inspections/import/ui.kt b/src/main/kotlin/org/rust/ide/inspections/import/ui.kt index 0143658240c..c08aad5a239 100644 --- a/src/main/kotlin/org/rust/ide/inspections/import/ui.kt +++ b/src/main/kotlin/org/rust/ide/inspections/import/ui.kt @@ -152,7 +152,7 @@ private class RsElementCellRenderer : DefaultPsiElementCellRenderer() { private fun textWithIcon(): Pair? { val crate = importCandidate?.qualifiedNamedItem?.containingCrate ?: return null return when (crate.origin) { - PackageOrigin.STDLIB -> crate.normName to RsIcons.RUST + PackageOrigin.STDLIB, PackageOrigin.STDLIB_DEPENDENCY -> crate.normName to RsIcons.RUST PackageOrigin.DEPENDENCY -> crate.normName to CargoIcons.ICON else -> null } diff --git a/src/main/kotlin/org/rust/ide/refactoring/RsImportOptimizer.kt b/src/main/kotlin/org/rust/ide/refactoring/RsImportOptimizer.kt index 0688cffef9f..47af5b5bb40 100644 --- a/src/main/kotlin/org/rust/ide/refactoring/RsImportOptimizer.kt +++ b/src/main/kotlin/org/rust/ide/refactoring/RsImportOptimizer.kt @@ -148,7 +148,7 @@ private class UseItemWrapper(val useItem: RsUseItem) { else -> when (basePath?.reference?.resolve()?.containingCrate?.origin) { PackageOrigin.WORKSPACE -> 3 PackageOrigin.DEPENDENCY -> 2 - PackageOrigin.STDLIB -> 1 + PackageOrigin.STDLIB, PackageOrigin.STDLIB_DEPENDENCY -> 1 null -> 3 } } diff --git a/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt b/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt index b8be1895164..bbb74a5c243 100644 --- a/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt +++ b/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt @@ -50,7 +50,9 @@ class CompilerFeature( } val crate = rsElement.containingCrate ?: return UNKNOWN - if (version.channel != RustChannel.NIGHTLY && crate.origin != PackageOrigin.STDLIB) return NOT_AVAILABLE + val origin = crate.origin + val isStdlibPart = origin == PackageOrigin.STDLIB || origin == PackageOrigin.STDLIB_DEPENDENCY + if (version.channel != RustChannel.NIGHTLY && !isStdlibPart) return NOT_AVAILABLE val cfgEvaluator = CfgEvaluator.forCrate(crate) val attrs = RsFeatureIndex.getFeatureAttributes(element.project, name) diff --git a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt index b6f5f2719ac..90bc43967bb 100644 --- a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt +++ b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt @@ -300,7 +300,7 @@ fun processExternCrateResolveVariants( NameResolutionTestmarks.shadowingStdCrates.hit() 0 } - PackageOrigin.STDLIB -> 1 + PackageOrigin.STDLIB, PackageOrigin.STDLIB_DEPENDENCY -> 1 } } for (dependency in explicitDepsFirst) { diff --git a/src/main/kotlin/org/rust/lang/utils/evaluation/CfgEvaluator.kt b/src/main/kotlin/org/rust/lang/utils/evaluation/CfgEvaluator.kt index 915631cf343..a79745fe7e8 100644 --- a/src/main/kotlin/org/rust/lang/utils/evaluation/CfgEvaluator.kt +++ b/src/main/kotlin/org/rust/lang/utils/evaluation/CfgEvaluator.kt @@ -164,7 +164,7 @@ class CfgEvaluator( fun forCrate(crate: Crate): CfgEvaluator { // `cfg(test)` evaluates to true only if there are no `cfg(not(test))` in the package val cfgTest = when (crate.origin) { - PackageOrigin.STDLIB -> False + PackageOrigin.STDLIB, PackageOrigin.STDLIB_DEPENDENCY -> False PackageOrigin.DEPENDENCY -> ThreeValuedLogic.fromBoolean( crate.cargoTarget?.pkg?.let { !RsCfgNotTestIndex.hasCfgNotTest(crate.cargoProject.project, it) } ?: false diff --git a/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt b/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt index 2a393cb86c9..63c96c10517 100644 --- a/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt +++ b/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt @@ -9,6 +9,7 @@ import org.rust.MinRustcVersion import org.rust.cargo.RsWithToolchainTestBase import org.rust.cargo.project.workspace.CargoWorkspace import org.rust.cargo.project.workspace.FeatureState +import org.rust.cargo.project.workspace.PackageOrigin import org.rust.ide.experiments.RsExperiments import org.rust.openapiext.runWithEnabledFeatures import org.rust.singleProject @@ -36,6 +37,7 @@ class CargoStdlibPackagesTest : RsWithToolchainTestBase() { val cargoProject = project.cargoProjects.singleProject() val workspace = cargoProject.workspaceOrFail() val hashbrownPkg = workspace.packages.first { it.name == "hashbrown" } + assertEquals(PackageOrigin.STDLIB_DEPENDENCY, hashbrownPkg.origin) hashbrownPkg.checkFeature("rustc-dep-of-std", FeatureState.Enabled) hashbrownPkg.checkFeature("default", FeatureState.Disabled) } From 2799ad8a78c63fdf67ae116a233a6cf83ee75276 Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Wed, 24 Feb 2021 22:32:09 +0300 Subject: [PATCH 4/7] CARGO: enable org.rust.cargo.fetch.actual.stdlib.metadata feature for nightly builds --- src/main/resources-nightly/META-INF/experiments.xml | 3 +++ src/main/resources-stable/META-INF/experiments.xml | 3 +++ src/main/resources/META-INF/rust-core.xml | 3 --- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/resources-nightly/META-INF/experiments.xml b/src/main/resources-nightly/META-INF/experiments.xml index 1c9d3131722..71af3bb2ebc 100644 --- a/src/main/resources-nightly/META-INF/experiments.xml +++ b/src/main/resources-nightly/META-INF/experiments.xml @@ -12,6 +12,9 @@ Run build scripts while project refresh. It allows collecting information about generated items like `cfg` options, environment variables, etc + + Fetch metadata of actual stdlib crates instead of using hardcoded data + Enables new macro expansion engine by default diff --git a/src/main/resources-stable/META-INF/experiments.xml b/src/main/resources-stable/META-INF/experiments.xml index 1a530431ccb..ab31649c50a 100644 --- a/src/main/resources-stable/META-INF/experiments.xml +++ b/src/main/resources-stable/META-INF/experiments.xml @@ -12,6 +12,9 @@ Run build scripts while project refresh. It allows collecting information about generated items like `cfg` options, environment variables, etc + + Fetch metadata of actual stdlib crates instead of using hardcoded data + Enables new macro expansion engine by default diff --git a/src/main/resources/META-INF/rust-core.xml b/src/main/resources/META-INF/rust-core.xml index 87a1cd29512..27f970d951c 100644 --- a/src/main/resources/META-INF/rust-core.xml +++ b/src/main/resources/META-INF/rust-core.xml @@ -1073,9 +1073,6 @@ Show settings gutter icon near [features] section in `Cargo.toml` where you can enable or disable all features - - Fetch metadata of actual stdlib crates instead of using hardcoded data - From 250d17fe44fa9b637f1e2db8237b24542744b475 Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Wed, 3 Mar 2021 21:41:48 +0300 Subject: [PATCH 5/7] T: add directory with stdlib dependencies into allowed roots in tests --- .../rust/cargo/project/workspace/StandardLibrary.kt | 2 +- src/main/kotlin/org/rust/openapiext/RsPathManager.kt | 1 + src/test/kotlin/org/rust/cargo/RustupTestFixture.kt | 11 ++++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt b/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt index 31f509d04cf..0d6202a9416 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/StandardLibrary.kt @@ -284,7 +284,7 @@ private class StdlibDataFetcher private constructor( val stdlibHash = stdlibHash(srcDir, version) - val stdlibVendor = RsPathManager.pluginDirInSystem().resolve("stdlib/${version.semver.parsedVersion}-$stdlibHash/vendor") + val stdlibVendor = RsPathManager.stdlibDependenciesDir().resolve("${version.semver.parsedVersion}-$stdlibHash/vendor") if (!stdlibVendor.exists()) { try { // `test` package depends on all other stdlib packages, diff --git a/src/main/kotlin/org/rust/openapiext/RsPathManager.kt b/src/main/kotlin/org/rust/openapiext/RsPathManager.kt index 2f56742b05e..f83ff678844 100644 --- a/src/main/kotlin/org/rust/openapiext/RsPathManager.kt +++ b/src/main/kotlin/org/rust/openapiext/RsPathManager.kt @@ -39,5 +39,6 @@ object RsPathManager { } fun pluginDirInSystem(): Path = Paths.get(PathManager.getSystemPath()).resolve("intellij-rust") + fun stdlibDependenciesDir(): Path = pluginDirInSystem().resolve("stdlib") fun tempPluginDirInSystem(): Path = Paths.get(PathManager.getTempPath()).resolve("intellij-rust") } diff --git a/src/test/kotlin/org/rust/cargo/RustupTestFixture.kt b/src/test/kotlin/org/rust/cargo/RustupTestFixture.kt index bb2185742e4..0eed103094a 100644 --- a/src/test/kotlin/org/rust/cargo/RustupTestFixture.kt +++ b/src/test/kotlin/org/rust/cargo/RustupTestFixture.kt @@ -15,6 +15,7 @@ import org.rust.cargo.toolchain.RsToolchain import org.rust.cargo.toolchain.tools.Rustup import org.rust.cargo.toolchain.tools.rustup import org.rust.cargo.util.DownloadResult +import org.rust.openapiext.RsPathManager import java.nio.file.Paths // TODO: use it in [org.rust.WithRustup] @@ -37,14 +38,16 @@ open class RustupTestFixture( override fun setUp() { super.setUp() - stdlib?.let { VfsRootAccess.allowRootAccess(testRootDisposable, it.path) } - addCargoHomeToAllowedRoots() + + setUpAllowedRoots() if (toolchain != null) { project.rustSettings.modifyTemporary(testRootDisposable) { it.toolchain = toolchain } } } - private fun addCargoHomeToAllowedRoots() { + private fun setUpAllowedRoots() { + stdlib?.let { VfsRootAccess.allowRootAccess(testRootDisposable, it.path) } + val cargoHome = Paths.get(FileUtil.expandUserHome("~/.cargo")) VfsRootAccess.allowRootAccess(testRootDisposable, cargoHome.toString()) // actions-rs/toolchain on CI creates symlink at `~/.cargo` while setting up of Rust toolchain @@ -52,5 +55,7 @@ open class RustupTestFixture( if (cargoHome != canonicalCargoHome) { VfsRootAccess.allowRootAccess(testRootDisposable, canonicalCargoHome.toString()) } + + VfsRootAccess.allowRootAccess(testRootDisposable, RsPathManager.stdlibDependenciesDir().toString()) } } From ec2bf421dddf28ab2d7c543c379c6d0010b1f2f7 Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Wed, 24 Mar 2021 12:55:53 +0300 Subject: [PATCH 6/7] T: disable check if feature state for stdlib dependencies for now --- .../cargo/project/workspace/CargoWorkspace.kt | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt b/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt index 0b93f6f02bf..4b1b98d77ad 100644 --- a/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt +++ b/src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt @@ -430,20 +430,28 @@ private class WorkspaceImpl( pkg.cargoEnabledFeatures.map { PackageFeature(pkg, it) } } for ((feature, state) in inferFeatureState(UserDisabledFeatures.EMPTY)) { - if (feature.pkg.origin == STDLIB) { - // All features of stdlib package should be considered as enabled by default. - // If `RsExperiments.FETCH_ACTUAL_STDLIB_METADATA` is enabled, - // the plugin invokes `cargo metadata` for `test` crate of stdlib to get actual info. - // And since stdlib packages are not a single workspace, - // `cargo` considers that all stdlib crates except test one are dependencies and - // as a result, enabled only features required by `test` package (i.e. `pkg.cargoEnabledFeatures` are not expected) - // It doesn't make sense to fix `pkg.cargoEnabledFeatures` so let's just adjust this check - if (!state.isEnabled) { - error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + when (feature.pkg.origin) { + STDLIB -> { + // All features of stdlib package should be considered as enabled by default. + // If `RsExperiments.FETCH_ACTUAL_STDLIB_METADATA` is enabled, + // the plugin invokes `cargo metadata` for `test` crate of stdlib to get actual info. + // And since stdlib packages are not a single workspace, + // `cargo` considers that all stdlib crates except test one are dependencies and + // as a result, enabled only features required by `test` package (i.e. `pkg.cargoEnabledFeatures` are not expected) + // It doesn't make sense to fix `pkg.cargoEnabledFeatures` so let's just adjust this check + if (!state.isEnabled) { + error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + } } - } else { - if (feature in enabledByCargo != state.isEnabled) { - error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + // `cargoEnabledFeatures` are not source of truth here when `RsExperiments.FETCH_ACTUAL_STDLIB_METADATA` is enabled + // because only `test` crate is considered as part of workspace where all features are enabled by default (see comment above). + // As a result, state of stdlib dependencies can differ from `cargoEnabledFeatures`. + // Skip check here for now + STDLIB_DEPENDENCY -> Unit + else -> { + if (feature in enabledByCargo != state.isEnabled) { + error("Feature `${feature.name}` in package `${feature.pkg.name}` should be ${!state}, but it is $state") + } } } } From b3db46fb03eb58b16730183835c4d56fcba2b76d Mon Sep 17 00:00:00 2001 From: Arseniy Pendryak Date: Fri, 26 Mar 2021 11:15:07 +0300 Subject: [PATCH 7/7] T: disable `org.rust.cargo.fetch.actual.stdlib.metadata` in test by default It significantly slows down tests without actual necessity. Enable this feature only in some tests that actually use/check it --- .../kotlin/org/rust/RustProjectDescriptors.kt | 16 ++++++++-------- .../org/rust/cargo/RsWithToolchainTestBase.kt | 9 +++++++++ src/test/kotlin/org/rust/common.kt | 10 ++++++++++ .../org/rustSlowTests/RsProjectViewTestBase.kt | 2 ++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/test/kotlin/org/rust/RustProjectDescriptors.kt b/src/test/kotlin/org/rust/RustProjectDescriptors.kt index 86e7766a879..cc5f077fe1e 100644 --- a/src/test/kotlin/org/rust/RustProjectDescriptors.kt +++ b/src/test/kotlin/org/rust/RustProjectDescriptors.kt @@ -44,13 +44,7 @@ object EmptyDescriptor : LightProjectDescriptor() object WithStdlibRustProjectDescriptor : WithRustup(DefaultDescriptor) -object WithActualStdlibRustProjectDescriptor : WithRustup(DefaultDescriptor) { - override fun testCargoProject(module: Module, contentRoot: String): CargoWorkspace { - return runWithEnabledFeatures(RsExperiments.FETCH_ACTUAL_STDLIB_METADATA) { - super.testCargoProject(module, contentRoot) - } - } -} +object WithActualStdlibRustProjectDescriptor : WithRustup(DefaultDescriptor, fetchActualStdlibMetadata = true) object WithStdlibAndDependencyRustProjectDescriptor : WithRustup(WithDependencyRustProjectDescriptor) @@ -112,7 +106,10 @@ open class RustProjectDescriptorBase : LightProjectDescriptor() { ) } -open class WithRustup(private val delegate: RustProjectDescriptorBase) : RustProjectDescriptorBase() { +open class WithRustup( + private val delegate: RustProjectDescriptorBase, + private val fetchActualStdlibMetadata: Boolean = false +) : RustProjectDescriptorBase() { private val toolchain: RsToolchain? by lazy { RsToolchain.suggest() } private val rustup by lazy { toolchain?.rustup(Paths.get(".")) } @@ -144,6 +141,9 @@ open class WithRustup(private val delegate: RustProjectDescriptorBase) : RustPro it.toolchain = toolchain } try { + // RsExperiments.FETCH_ACTUAL_STDLIB_METADATA significantly slows down tests + setExperimentalFeatureEnabled(RsExperiments.FETCH_ACTUAL_STDLIB_METADATA, fetchActualStdlibMetadata, disposable) + val rustcInfo = rustcInfo val stdlib = StandardLibrary.fromFile(module.project, stdlib!!, rustcInfo)!! val cfgOptions = cfgOptions!! diff --git a/src/test/kotlin/org/rust/cargo/RsWithToolchainTestBase.kt b/src/test/kotlin/org/rust/cargo/RsWithToolchainTestBase.kt index f4ada41530b..b8b0d68d8f8 100644 --- a/src/test/kotlin/org/rust/cargo/RsWithToolchainTestBase.kt +++ b/src/test/kotlin/org/rust/cargo/RsWithToolchainTestBase.kt @@ -5,6 +5,7 @@ package org.rust.cargo +import com.intellij.openapi.Disposable import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.RecursionManager import com.intellij.openapi.vfs.VirtualFile @@ -16,6 +17,7 @@ import com.intellij.util.ui.UIUtil import org.rust.* import org.rust.cargo.project.model.impl.testCargoProjects import org.rust.cargo.toolchain.tools.rustc +import org.rust.ide.experiments.RsExperiments import org.rust.lang.core.macros.macroExpansionManager import org.rust.openapiext.pathAsPath @@ -32,6 +34,7 @@ abstract class RsWithToolchainTestBase : CodeInsightFixtureTestCase Unit): TestProject = fileTree { builder() }.create() diff --git a/src/test/kotlin/org/rust/common.kt b/src/test/kotlin/org/rust/common.kt index d1e6e5b655f..12075448680 100644 --- a/src/test/kotlin/org/rust/common.kt +++ b/src/test/kotlin/org/rust/common.kt @@ -5,11 +5,13 @@ package org.rust +import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.DataKey import com.intellij.openapi.actionSystem.Presentation import com.intellij.openapi.ui.TestDialog import com.intellij.openapi.ui.TestDialogManager +import com.intellij.openapi.util.Disposer import com.intellij.testFramework.TestApplicationManager import com.intellij.testFramework.TestDataProvider import com.intellij.testFramework.fixtures.CodeInsightTestFixture @@ -21,6 +23,8 @@ import org.rust.cargo.project.workspace.FeatureName import org.rust.cargo.project.workspace.FeatureState import org.rust.lang.core.macros.MACRO_EXPANSION_VFS_ROOT import org.rust.lang.core.macros.MacroExpansionFileSystem +import org.rust.openapiext.isFeatureEnabled +import org.rust.openapiext.setFeatureEnabled fun checkMacroExpansionFileSystemAfterTest() { val vfs = MacroExpansionFileSystem.getInstance() @@ -113,3 +117,9 @@ fun withTestDialog(testDialog: TestDialog, action: () -> T): T { TestDialogManager.setTestDialog(oldDialog) } } + +fun setExperimentalFeatureEnabled(featureId: String, enabled: Boolean, disposable: Disposable) { + val oldValue = isFeatureEnabled(featureId) + setFeatureEnabled(featureId, enabled) + Disposer.register(disposable) { setFeatureEnabled(featureId, oldValue) } +} diff --git a/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt b/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt index 75cf150ddd3..412a0332cab 100644 --- a/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt +++ b/src/test/kotlin/org/rustSlowTests/RsProjectViewTestBase.kt @@ -18,6 +18,8 @@ import javax.swing.tree.TreePath abstract class RsProjectViewTestBase : RsWithToolchainTestBase() { + override val fetchActualStdlibMetadata: Boolean get() = true + override fun setUp() { super.setUp() ProjectViewTestUtil.setupImpl(project, true)