From de90c388fc1581f5b25ff8e46bb7a45c0ec33121 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 19 Jul 2020 18:17:36 -0400 Subject: [PATCH 1/2] Ban empty feature names in attributes --- src/librustc_attr/builtin.rs | 8 ++++++++ src/librustc_span/symbol.rs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs index 0606fac2fe748..b2533fdf35d83 100644 --- a/src/librustc_attr/builtin.rs +++ b/src/librustc_attr/builtin.rs @@ -21,6 +21,7 @@ enum AttrError { MultipleItem(String), UnknownMetaItem(String, &'static [&'static str]), MissingSince, + EmptyFeature, MissingFeature, MultipleStabilityLevels, UnsupportedLiteral(&'static str, /* is_bytestr */ bool), @@ -41,6 +42,9 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) { AttrError::MissingSince => { struct_span_err!(diag, span, E0542, "missing 'since'").emit(); } + AttrError::EmptyFeature => { + struct_span_err!(diag, span, E0546, "empty 'feature' attribute").emit(); + } AttrError::MissingFeature => { struct_span_err!(diag, span, E0546, "missing 'feature'").emit(); } @@ -427,6 +431,10 @@ where match (feature, reason, issue) { (Some(feature), reason, Some(_)) => { + if feature.is_empty() { + handle_errors(sess, attr.span, AttrError::EmptyFeature); + continue; + } let level = Unstable { reason, issue: issue_num, is_soft }; if sym::unstable == meta_name { stab = Some(Stability { level, feature, rustc_depr: None }); diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 5d332ddf5f3df..5ff4f9a8282d4 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -1435,6 +1435,11 @@ impl Symbol { pub fn to_ident_string(self) -> String { Ident::with_dummy_span(self).to_string() } + + pub fn is_empty(self) -> bool { + // Invalid is "" (the empty string) + self == kw::Invalid + } } impl fmt::Debug for Symbol { From a2080dcae183d7b7d29f47df3f4a223221c0b00f Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 19 Jul 2020 18:20:01 -0400 Subject: [PATCH 2/2] Feature gate is always present --- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/render.rs | 30 +++++++++++++++--------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8a4ee91df405f..d35817af43c28 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2348,7 +2348,7 @@ impl Clean for attr::Stability { fn clean(&self, _: &DocContext<'_>) -> Stability { Stability { level: stability::StabilityLevel::from_attr_level(&self.level), - feature: Some(self.feature.to_string()).filter(|f| !f.is_empty()), + feature: self.feature.to_string(), since: match self.level { attr::Stable { ref since } => since.to_string(), _ => String::new(), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6a03722cd0802..a3cd03230037a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1526,7 +1526,7 @@ pub struct ProcMacro { #[derive(Clone, Debug)] pub struct Stability { pub level: stability::StabilityLevel, - pub feature: Option, + pub feature: String, pub since: String, pub deprecation: Option, pub unstable_reason: Option, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index f872ed7010c75..0ba9d19c939b0 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2235,7 +2235,7 @@ fn stability_tags(item: &clean::Item) -> String { if item .stability .as_ref() - .map(|s| s.level == stability::Unstable && s.feature.as_deref() != Some("rustc_private")) + .map(|s| s.level == stability::Unstable && s.feature != "rustc_private") == Some(true) { tags += &tag_html("unstable", "Experimental"); @@ -2291,25 +2291,25 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. - if let Some(stab) = item.stability.as_ref().filter(|stab| { - stab.level == stability::Unstable && stab.feature.as_deref() != Some("rustc_private") - }) { + if let Some(stab) = item + .stability + .as_ref() + .filter(|stab| stab.level == stability::Unstable && stab.feature != "rustc_private") + { let mut message = "🔬 This is a nightly-only experimental API.".to_owned(); - if let Some(feature) = stab.feature.as_deref() { - let mut feature = format!("{}", Escape(&feature)); - if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) { - feature.push_str(&format!( - " #{issue}", - url = url, - issue = issue - )); - } - - message.push_str(&format!(" ({})", feature)); + let mut feature = format!("{}", Escape(&stab.feature)); + if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) { + feature.push_str(&format!( + " #{issue}", + url = url, + issue = issue + )); } + message.push_str(&format!(" ({})", feature)); + if let Some(unstable_reason) = &stab.unstable_reason { let mut ids = cx.id_map.borrow_mut(); message = format!(