diff --git a/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt b/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt index 2f5eede3a73..11a97f88c9d 100644 --- a/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt +++ b/src/main/kotlin/org/rust/lang/core/CompilerFeature.kt @@ -14,12 +14,11 @@ import org.rust.ide.annotator.RsAnnotationHolder import org.rust.ide.annotator.fixes.AddFeatureAttributeFix import org.rust.lang.core.FeatureAvailability.* import org.rust.lang.core.FeatureState.ACCEPTED -import org.rust.lang.core.psi.ext.RsElement -import org.rust.lang.core.psi.ext.ancestorOrSelf -import org.rust.lang.core.psi.ext.cargoProject +import org.rust.lang.core.psi.ext.* import org.rust.lang.core.stubs.index.RsFeatureIndex import org.rust.lang.utils.RsDiagnostic import org.rust.lang.utils.addToHolder +import org.rust.lang.utils.evaluation.CfgEvaluator class CompilerFeature( val name: String, @@ -48,9 +47,19 @@ class CompilerFeature( if (state == ACCEPTED && (since == null || version.semver >= since)) return AVAILABLE if (version.channel != RustChannel.NIGHTLY) return NOT_AVAILABLE - val crateRoot = rsElement.crateRoot ?: return UNKNOWN + val crate = rsElement.containingCrate ?: return UNKNOWN + val cfgEvaluator = CfgEvaluator.forCrate(crate) val attrs = RsFeatureIndex.getFeatureAttributes(element.project, name) - return if (attrs.any { it.crateRoot == crateRoot }) AVAILABLE else CAN_BE_ADDED + val possibleFeatureAttrs = attrs.asSequence() + .filter { it.containingCrate == crate } + .flatMap { cfgEvaluator.expandCfgAttrs(sequenceOf(it.metaItem)) } + + for (featureAttr in possibleFeatureAttrs) { + if (featureAttr.name != "feature") continue + val metaItems = featureAttr.metaItemArgs?.metaItemList.orEmpty() + if (metaItems.any { feature -> feature.name == name }) return AVAILABLE + } + return CAN_BE_ADDED } fun check( diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsDocAndAttributeOwner.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsDocAndAttributeOwner.kt index e32478e11ca..48b4fda81cc 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsDocAndAttributeOwner.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsDocAndAttributeOwner.kt @@ -111,7 +111,7 @@ private fun RsDocAndAttributeOwner.getExpandedAttributesNoStub(explicitCrate: Cr return QueryAttributes(evaluator.expandCfgAttrs(rawMetaItems)) } -private fun CfgEvaluator.expandCfgAttrs(rawMetaItems: Sequence): Sequence { +fun CfgEvaluator.expandCfgAttrs(rawMetaItems: Sequence): Sequence { return rawMetaItems.flatMap { if (it.name == "cfg_attr") { val list = it.metaItemArgs?.metaItemList?.iterator() ?: return@flatMap emptySequence() diff --git a/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt b/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt index 7c6610dfebf..d355a76a70e 100644 --- a/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt +++ b/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt @@ -49,7 +49,7 @@ class RsFileStub( override fun getType() = Type object Type : IStubFileElementType(RsLanguage) { - private const val STUB_VERSION = 206 + private const val STUB_VERSION = 207 // Bump this number if Stub structure changes override fun getStubVersion(): Int = RustParserDefinition.PARSER_VERSION + STUB_VERSION diff --git a/src/main/kotlin/org/rust/lang/core/stubs/index/RsFeatureIndex.kt b/src/main/kotlin/org/rust/lang/core/stubs/index/RsFeatureIndex.kt index d313cdb9530..0ef834a8812 100644 --- a/src/main/kotlin/org/rust/lang/core/stubs/index/RsFeatureIndex.kt +++ b/src/main/kotlin/org/rust/lang/core/stubs/index/RsFeatureIndex.kt @@ -11,6 +11,7 @@ import com.intellij.psi.stubs.IndexSink import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndexKey import org.rust.lang.core.psi.RsInnerAttr +import org.rust.lang.core.psi.RsMetaItem import org.rust.lang.core.psi.ext.name import org.rust.lang.core.stubs.RsFileStub import org.rust.lang.core.stubs.RsInnerAttrStub @@ -28,15 +29,32 @@ class RsFeatureIndex : StringStubIndexExtension() { fun index(stub: RsInnerAttrStub, sink: IndexSink) { val metaItem = stub.psi.metaItem - if (metaItem.name == "feature") { - val features = metaItem.metaItemArgs?.metaItemList.orEmpty() - for (feature in features) { - val featureName = feature.name ?: continue - sink.occurrence(KEY, featureName) + index(metaItem, sink) + } + + private fun index(metaItem: RsMetaItem, sink: IndexSink) { + when (metaItem.name) { + "feature" -> { + val features = metaItem.metaItemArgs?.metaItemList.orEmpty() + for (feature in features) { + val featureName = feature.name ?: continue + sink.occurrence(KEY, featureName) + } + } + "cfg_attr" -> { + val children = metaItem.metaItemArgs?.metaItemList.orEmpty().drop(1) + for (child in children) { + index(child, sink) + } } } } + /** + * Returns collection of [RsInnerAttr] contained `feature` attribute with desired [featureName] + * including `feature` attributes under `cfg_attr`. + * Note, it doesn't evaluate `cfg_attr` + */ fun getFeatureAttributes(project: Project, featureName: String): Collection { checkCommitIsNotInProgress(project) return getElements(KEY, featureName, project, GlobalSearchScope.allScope(project)) diff --git a/src/test/kotlin/org/rust/ide/annotator/RsErrorAnnotatorTest.kt b/src/test/kotlin/org/rust/ide/annotator/RsErrorAnnotatorTest.kt index d9aca089861..8d5d925f320 100644 --- a/src/test/kotlin/org/rust/ide/annotator/RsErrorAnnotatorTest.kt +++ b/src/test/kotlin/org/rust/ide/annotator/RsErrorAnnotatorTest.kt @@ -1591,6 +1591,30 @@ class RsErrorAnnotatorTest : RsAnnotatorTestBase(RsErrorAnnotator::class) { } """) + @MockAdditionalCfgOptions("intellij_rust") + @MockRustcVersion("1.29.0-nightly") + fun `test crate visibility feature E0658 under cfg_attr 1`() = checkErrors(""" + #![cfg_attr(intellij_rust, feature(crate_visibility_modifier))] + + crate struct Foo; + """) + + @MockAdditionalCfgOptions("intellij_rust") + @MockRustcVersion("1.29.0-nightly") + fun `test crate visibility feature E0658 under cfg_attr 2`() = checkErrors(""" + #![cfg_attr(not(intellij_rust), feature(crate_visibility_modifier))] + + crate struct Foo; + """) + + @MockAdditionalCfgOptions("intellij_rust") + @MockRustcVersion("1.29.0-nightly") + fun `test crate visibility feature E0658 under nested cfg_attr`() = checkErrors(""" + #![cfg_attr(intellij_rust, cfg_attr(intellij_rust, feature(crate_visibility_modifier)))] + + crate struct Foo; + """) + fun `test parenthesized lifetime bounds`() = checkErrors(""" fn foo<'a, T: ('a)>(t: T) { unimplemented!(); @@ -1714,6 +1738,28 @@ class RsErrorAnnotatorTest : RsAnnotatorTestBase(RsErrorAnnotator::class) { } """) + @MockAdditionalCfgOptions("intellij_rust") + @MockRustcVersion("1.0.0-nightly") + fun `test box expression feature E0658 with cfg_attr with several attributes 1`() = checkErrors(""" + #![cfg_attr(intellij_rust, feature(generators), feature(box_syntax))] + + struct S; + fn main() { + let x = box S; + } + """) + + @MockAdditionalCfgOptions("intellij_rust") + @MockRustcVersion("1.0.0-nightly") + fun `test box expression feature E0658 with cfg_attr with several attributes 2`() = checkErrors(""" + #![cfg_attr(not(intellij_rust), feature(generators), feature(box_syntax))] + + struct S; + fn main() { + let x = box S; + } + """) + @MockRustcVersion("1.41.0") fun `test raw address of feature E0658 1`() = checkErrors(""" fn main() {