Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Take into account cfg_attr during experimental compiler features annotation #6636

Merged
merged 1 commit into from Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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