Skip to content

Add empty impl blocks if they have documentation #90905

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

Merged
merged 4 commits into from
Jun 6, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions src/etc/htmldocck.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@
in the specified file. The number of occurrences must match the given
count.

* `@count PATH XPATH TEXT COUNT` checks for the occurrence of the given XPath
with the given text in the specified file. The number of occurrences must
match the given count.

* `@snapshot NAME PATH XPATH` creates a snapshot test named NAME.
A snapshot test captures a subtree of the DOM, at the location
determined by the XPath, and compares it to a pre-recorded value
Expand Down Expand Up @@ -382,23 +386,25 @@ def check_tree_attr(tree, path, attr, pat, regexp):
return ret


def check_tree_text(tree, path, pat, regexp):
# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`).
def check_tree_text(tree, path, pat, regexp, stop_at_first):
path = normalize_xpath(path)
ret = False
match_count = 0
try:
for e in tree.findall(path):
try:
value = flatten(e)
except KeyError:
continue
else:
ret = check_string(value, pat, regexp)
if ret:
break
if check_string(value, pat, regexp):
match_count += 1
if stop_at_first:
break
except Exception:
print('Failed to get path "{}"'.format(path))
raise
return ret
return match_count


def get_tree_count(tree, path):
Expand Down Expand Up @@ -516,6 +522,19 @@ def print_err(lineno, context, err, message=None):
stderr("\t{}".format(context))


def get_nb_matching_elements(cache, c, regexp, stop_at_first):
tree = cache.get_tree(c.args[0])
pat, sep, attr = c.args[1].partition('/@')
if sep: # attribute
tree = cache.get_tree(c.args[0])
return check_tree_attr(tree, pat, attr, c.args[2], False)
else: # normalized text
pat = c.args[1]
if pat.endswith('/text()'):
pat = pat[:-7]
return check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first)


ERR_COUNT = 0


Expand All @@ -536,16 +555,7 @@ def check_command(c, cache):
ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
elif len(c.args) == 3: # @has/matches <path> <pat> <match> = XML tree test
cerr = "`XPATH PATTERN` did not match"
tree = cache.get_tree(c.args[0])
pat, sep, attr = c.args[1].partition('/@')
if sep: # attribute
tree = cache.get_tree(c.args[0])
ret = check_tree_attr(tree, pat, attr, c.args[2], regexp)
else: # normalized text
pat = c.args[1]
if pat.endswith('/text()'):
pat = pat[:-7]
ret = check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp)
ret = get_nb_matching_elements(cache, c, regexp, True) != 0
else:
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))

Expand All @@ -555,6 +565,11 @@ def check_command(c, cache):
found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
cerr = "Expected {} occurrences but found {}".format(expected, found)
ret = expected == found
elif len(c.args) == 4: # @count <path> <pat> <text> <count> = count test
expected = int(c.args[3])
found = get_nb_matching_elements(cache, c, False, False)
cerr = "Expected {} occurrences but found {}".format(expected, found)
ret = found == expected
else:
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))

Expand Down
7 changes: 7 additions & 0 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,13 @@ fn render_impl(
}

if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
if trait_.is_none() && i.inner_impl().items.is_empty() {
w.write_str(
"<div class=\"item-info\">\
<div class=\"stab empty-impl\">This impl block contains no items.</div>
</div>",
);
}
write!(
w,
"<div class=\"docblock\">{}</div>",
Expand Down
6 changes: 5 additions & 1 deletion src/librustdoc/html/static/css/themes/ayu.css
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,13 @@ details.undocumented > summary::before {
color: #000;
}

/* Created this empty rule to satisfy the theme checks. */
.stab.empty-impl {}

.stab.unstable,
.stab.deprecated,
.stab.portability {
.stab.portability,
.stab.empty-impl {
color: #c5c5c5;
background: #314559 !important;
border-style: none !important;
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/html/static/css/themes/dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ details.undocumented > summary::before {
color: #ddd;
}

.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; }
.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/html/static/css/themes/light.css
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ details.undocumented > summary::before {
color: #000;
}

.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; }
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
.stab.portability { background: #F3DFFF; border-color: #b07bdb; }
Expand Down
5 changes: 3 additions & 2 deletions src/librustdoc/passes/stripper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,9 @@ pub(crate) struct ImplStripper<'a> {
impl<'a> DocFolder for ImplStripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(ref imp) = *i.kind {
// emptied none trait impls can be stripped
if imp.trait_.is_none() && imp.items.is_empty() {
// Impl blocks can be skipped if they are: empty; not a trait impl; and have no
// documentation.
if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
return None;
}
if let Some(did) = imp.for_.def_id(self.cache) {
Expand Down
20 changes: 20 additions & 0 deletions src/test/rustdoc/empty-impl-block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![crate_name = "foo"]

// @has 'foo/struct.Foo.html'
pub struct Foo;

// @has - '//*[@class="docblock"]' 'Hello empty impl block!'
// @has - '//*[@class="item-info"]' 'This impl block contains no items.'
/// Hello empty impl block!
impl Foo {}
// We ensure that this empty impl block without doc isn't rendered.
// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 1
impl Foo {}

// Just to ensure that empty trait impl blocks are rendered.
pub struct Another;
pub trait Bar {}

// @has 'foo/struct.Another.html'
// @has - '//h3[@class="code-header in-band"]' 'impl Bar for Another'
impl Bar for Another {}