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

RFC 2008 non-exhaustive enums/structs: Rustdoc #51854

Merged
merged 8 commits into from Jul 19, 2018
5 changes: 5 additions & 0 deletions src/librustdoc/clean/mod.rs
Expand Up @@ -495,6 +495,11 @@ impl Item {
self.stability.as_ref().map(|s| &s.since[..])
}

pub fn is_non_exhaustive(&self) -> bool {
self.attrs.other_attrs.iter()
.any(|a| a.name().as_str() == "non_exhaustive")
}

/// Returns a documentation-level item type from the item.
pub fn type_(&self) -> ItemType {
ItemType::from(self)
Expand Down
11 changes: 10 additions & 1 deletion src/librustdoc/fold.rs
Expand Up @@ -90,7 +90,16 @@ pub trait DocFolder : Sized {

/// don't override!
fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item;
let Item {
attrs,
name,
source,
visibility,
def_id,
inner,
stability,
deprecation,
} = item;

let inner = match inner {
StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)),
Expand Down
42 changes: 39 additions & 3 deletions src/librustdoc/html/render.rs
Expand Up @@ -2262,6 +2262,37 @@ fn document_stability(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item)
Ok(())
}

fn document_non_exhaustive_header(item: &clean::Item) -> &str {
if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
}

fn document_non_exhaustive(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
if item.is_non_exhaustive() {
write!(w, "<div class='docblock non-exhaustive non-exhaustive-{}'>", {
if item.is_struct() { "struct" } else if item.is_enum() { "enum" } else { "type" }
})?;

if item.is_struct() {
write!(w, "Non-exhaustive structs could have additional fields added in future. \
Therefore, non-exhaustive structs cannot be constructed in external crates \
using the traditional <code>Struct {{ .. }}</code> syntax; cannot be \
matched against without a wildcard <code>..</code>; and \
functional-record-updates do not work.")?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "functional-record-updates" a term we use elsewhere? I'm not familiar with what it could mean.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the book refers to it as struct update syntax, I'll change the PR to reflect this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a commit that changes this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The book actually didn't mention FRU at all so I was equally confused when the RFC came about. Struct update syntax is a much better way of putting it, and I'm glad it's finally in the book now.

} else if item.is_enum() {
write!(w, "Non-exhaustive enums could have additional variants added in future. \
Therefore, when matching against variants of non-exhaustive enums, an \
extra wildcard arm must be added to account for any future variants.")?;
} else {
write!(w, "This type will require a wildcard arm in any match statements or \
constructors.")?;
}

write!(w, "</div>")?;
}

Ok(())
}

fn name_key(name: &str) -> (&str, u64, usize) {
// find number at end
let split = name.bytes().rposition(|b| b < b'0' || b'9' < b).map_or(0, |s| s + 1);
Expand Down Expand Up @@ -3122,7 +3153,9 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
if let doctree::Plain = s.struct_type {
if fields.peek().is_some() {
write!(w, "<h2 id='fields' class='fields small-section-header'>
Fields<a href='#fields' class='anchor'></a></h2>")?;
Fields{}<a href='#fields' class='anchor'></a></h2>",
document_non_exhaustive_header(it))?;
document_non_exhaustive(w, it)?;
for (field, ty) in fields {
let id = derive_id(format!("{}.{}",
ItemType::StructField,
Expand Down Expand Up @@ -3254,7 +3287,9 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
document(w, cx, it)?;
if !e.variants.is_empty() {
write!(w, "<h2 id='variants' class='variants small-section-header'>
Variants<a href='#variants' class='anchor'></a></h2>\n")?;
Variants{}<a href='#variants' class='anchor'></a></h2>\n",
document_non_exhaustive_header(it))?;
document_non_exhaustive(w, it)?;
for variant in &e.variants {
let id = derive_id(format!("{}.{}",
ItemType::Variant,
Expand Down Expand Up @@ -3355,7 +3390,8 @@ const ATTRIBUTE_WHITELIST: &'static [&'static str] = &[
"must_use",
"no_mangle",
"repr",
"unsafe_destructor_blind_to_params"
"unsafe_destructor_blind_to_params",
"non_exhaustive"
];

fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
Expand Down
23 changes: 20 additions & 3 deletions src/librustdoc/html/static/main.js
Expand Up @@ -1993,15 +1993,18 @@
onEach(e.getElementsByClassName('associatedconstant'), func);
});

function createToggle(otherMessage, extraClass) {
function createToggle(otherMessage, fontSize, extraClass) {
var span = document.createElement('span');
span.className = 'toggle-label';
span.style.display = 'none';
if (!otherMessage) {
span.innerHTML = '&nbsp;Expand&nbsp;description';
} else {
span.innerHTML = otherMessage;
span.style.fontSize = '20px';
}

if (fontSize) {
span.style.fontSize = fontSize;
}

var mainToggle = toggle.cloneNode(true);
Expand Down Expand Up @@ -2040,13 +2043,27 @@
}
if (e.parentNode.id === "main") {
var otherMessage;
var fontSize;
var extraClass;

if (hasClass(e, "type-decl")) {
fontSize = "20px";
otherMessage = '&nbsp;Show&nbsp;declaration';
} else if (hasClass(e, "non-exhaustive")) {
otherMessage = '&nbsp;This&nbsp;';
if (hasClass(e, "non-exhaustive-struct")) {
otherMessage += 'struct';
} else if (hasClass(e, "non-exhaustive-enum")) {
otherMessage += 'enum';
} else if (hasClass(e, "non-exhaustive-type")) {
otherMessage += 'type';
}
otherMessage += '&nbsp;is&nbsp;marked&nbsp;as&nbsp;non-exhaustive';
} else if (hasClass(e.childNodes[0], "impl-items")) {
extraClass = "marg-left";
}
e.parentNode.insertBefore(createToggle(otherMessage, extraClass), e);

e.parentNode.insertBefore(createToggle(otherMessage, fontSize, extraClass), e);
if (otherMessage && getCurrentValue('rustdoc-item-declarations') !== "false") {
collapseDocs(e.previousSibling.childNodes[0], "toggle");
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustdoc/html/static/rustdoc.css
Expand Up @@ -1358,4 +1358,8 @@ kbd {
}
#all-types > p {
margin: 5px 0;
}
}

.non-exhaustive {
margin-bottom: 1em;
}
2 changes: 1 addition & 1 deletion src/librustdoc/html/static/themes/dark.css
Expand Up @@ -406,4 +406,4 @@ kbd {
}
.search-results td span.grey {
color: #ccc;
}
}
2 changes: 1 addition & 1 deletion src/librustdoc/html/static/themes/light.css
Expand Up @@ -400,4 +400,4 @@ kbd {
}
.search-results td span.grey {
color: #999;
}
}