Skip to content

Commit

Permalink
Merge #6636
Browse files Browse the repository at this point in the history
6636: Take into account cfg_attr during experimental compiler features annotation r=vlad20012 a=Undin

Previously, the corresponding annotations took into account only top-level `feature` attributes.
Now, they correctly expand `cfg_attr` attribute

Continuation of #6497
Fixes #6631

changelog: Take into account `cfg_attr` attribute during experimental compiler features annotation 


Co-authored-by: Arseniy Pendryak <a.pendryak@yandex.ru>
  • Loading branch information
bors[bot] and Undin committed Jan 12, 2021
2 parents 88d71f7 + 960acd2 commit e009508
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 12 deletions.
19 changes: 14 additions & 5 deletions src/main/kotlin/org/rust/lang/core/CompilerFeature.kt
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
Expand Up @@ -111,7 +111,7 @@ private fun RsDocAndAttributeOwner.getExpandedAttributesNoStub(explicitCrate: Cr
return QueryAttributes(evaluator.expandCfgAttrs(rawMetaItems))
}

private fun CfgEvaluator.expandCfgAttrs(rawMetaItems: Sequence<RsMetaItem>): Sequence<RsMetaItem> {
fun CfgEvaluator.expandCfgAttrs(rawMetaItems: Sequence<RsMetaItem>): Sequence<RsMetaItem> {
return rawMetaItems.flatMap {
if (it.name == "cfg_attr") {
val list = it.metaItemArgs?.metaItemList?.iterator() ?: return@flatMap emptySequence()
Expand Down
Expand Up @@ -49,7 +49,7 @@ class RsFileStub(
override fun getType() = Type

object Type : IStubFileElementType<RsFileStub>(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
Expand Down
28 changes: 23 additions & 5 deletions src/main/kotlin/org/rust/lang/core/stubs/index/RsFeatureIndex.kt
Expand Up @@ -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
Expand All @@ -28,15 +29,32 @@ class RsFeatureIndex : StringStubIndexExtension<RsInnerAttr>() {

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<RsInnerAttr> {
checkCommitIsNotInProgress(project)
return getElements(KEY, featureName, project, GlobalSearchScope.allScope(project))
Expand Down
46 changes: 46 additions & 0 deletions src/test/kotlin/org/rust/ide/annotator/RsErrorAnnotatorTest.kt
Expand Up @@ -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))]
<error descr="`crate` visibility modifier is experimental [E0658]">crate</error> 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: <error descr="Parenthesized lifetime bounds are not supported">('a)</error>>(t: T) {
unimplemented!();
Expand Down Expand Up @@ -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 = <error descr="`box` expression syntax is experimental [E0658]">box</error> S;
}
""")

@MockRustcVersion("1.41.0")
fun `test raw address of feature E0658 1`() = checkErrors("""
fn main() {
Expand Down

0 comments on commit e009508

Please sign in to comment.