Rollup merge of #90905 - GuillaumeGomez:empty-impl-blocks, r=jsha
Add empty impl blocks if they have documentation Fixes https://github.com/rust-lang/rust/issues/90866. The update for the test script is needed to count the number of impl blocks we have with only the struct. To be noted that with https://github.com/rust-lang/rust/pull/89676 merged, it wouldn't be needed (I don't know what is the status of it btw. cc ```@Mark-Simulacrum).``` It looks like this:  cc ```@jyn514``` r? ```@camelid```
This commit is contained in:
commit
77f0209fde
7 changed files with 68 additions and 19 deletions
|
@ -94,6 +94,10 @@ There are a number of supported commands:
|
||||||
in the specified file. The number of occurrences must match the given
|
in the specified file. The number of occurrences must match the given
|
||||||
count.
|
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.
|
* `@snapshot NAME PATH XPATH` creates a snapshot test named NAME.
|
||||||
A snapshot test captures a subtree of the DOM, at the location
|
A snapshot test captures a subtree of the DOM, at the location
|
||||||
determined by the XPath, and compares it to a pre-recorded value
|
determined by the XPath, and compares it to a pre-recorded value
|
||||||
|
@ -382,9 +386,10 @@ def check_tree_attr(tree, path, attr, pat, regexp):
|
||||||
return ret
|
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)
|
path = normalize_xpath(path)
|
||||||
ret = False
|
match_count = 0
|
||||||
try:
|
try:
|
||||||
for e in tree.findall(path):
|
for e in tree.findall(path):
|
||||||
try:
|
try:
|
||||||
|
@ -392,13 +397,14 @@ def check_tree_text(tree, path, pat, regexp):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
ret = check_string(value, pat, regexp)
|
if check_string(value, pat, regexp):
|
||||||
if ret:
|
match_count += 1
|
||||||
break
|
if stop_at_first:
|
||||||
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
print('Failed to get path "{}"'.format(path))
|
print('Failed to get path "{}"'.format(path))
|
||||||
raise
|
raise
|
||||||
return ret
|
return match_count
|
||||||
|
|
||||||
|
|
||||||
def get_tree_count(tree, path):
|
def get_tree_count(tree, path):
|
||||||
|
@ -518,6 +524,19 @@ def print_err(lineno, context, err, message=None):
|
||||||
stderr("\t{}".format(context))
|
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
|
ERR_COUNT = 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -538,16 +557,7 @@ def check_command(c, cache):
|
||||||
ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
|
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
|
elif len(c.args) == 3: # @has/matches <path> <pat> <match> = XML tree test
|
||||||
cerr = "`XPATH PATTERN` did not match"
|
cerr = "`XPATH PATTERN` did not match"
|
||||||
tree = cache.get_tree(c.args[0])
|
ret = get_nb_matching_elements(cache, c, regexp, True) != 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)
|
|
||||||
else:
|
else:
|
||||||
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
||||||
|
|
||||||
|
@ -557,6 +567,11 @@ def check_command(c, cache):
|
||||||
found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
|
found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
|
||||||
cerr = "Expected {} occurrences but found {}".format(expected, found)
|
cerr = "Expected {} occurrences but found {}".format(expected, found)
|
||||||
ret = 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:
|
else:
|
||||||
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
||||||
|
|
||||||
|
|
|
@ -1600,6 +1600,13 @@ fn render_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
|
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!(
|
write!(
|
||||||
w,
|
w,
|
||||||
"<div class=\"docblock\">{}</div>",
|
"<div class=\"docblock\">{}</div>",
|
||||||
|
|
|
@ -281,9 +281,13 @@ details.undocumented > summary::before {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Created this empty rule to satisfy the theme checks. */
|
||||||
|
.stab.empty-impl {}
|
||||||
|
|
||||||
.stab.unstable,
|
.stab.unstable,
|
||||||
.stab.deprecated,
|
.stab.deprecated,
|
||||||
.stab.portability {
|
.stab.portability,
|
||||||
|
.stab.empty-impl {
|
||||||
color: #c5c5c5;
|
color: #c5c5c5;
|
||||||
background: #314559 !important;
|
background: #314559 !important;
|
||||||
border-style: none !important;
|
border-style: none !important;
|
||||||
|
|
|
@ -266,6 +266,7 @@ details.undocumented > summary::before {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
|
||||||
.stab.unstable { 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.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; }
|
||||||
.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
|
.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
|
||||||
|
|
|
@ -255,6 +255,7 @@ details.undocumented > summary::before {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; }
|
||||||
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
||||||
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
|
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
|
||||||
.stab.portability { background: #F3DFFF; border-color: #b07bdb; }
|
.stab.portability { background: #F3DFFF; border-color: #b07bdb; }
|
||||||
|
|
|
@ -124,8 +124,9 @@ pub(crate) struct ImplStripper<'a> {
|
||||||
impl<'a> DocFolder for ImplStripper<'a> {
|
impl<'a> DocFolder for ImplStripper<'a> {
|
||||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||||
if let clean::ImplItem(ref imp) = *i.kind {
|
if let clean::ImplItem(ref imp) = *i.kind {
|
||||||
// emptied none trait impls can be stripped
|
// Impl blocks can be skipped if they are: empty; not a trait impl; and have no
|
||||||
if imp.trait_.is_none() && imp.items.is_empty() {
|
// documentation.
|
||||||
|
if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let Some(did) = imp.for_.def_id(self.cache) {
|
if let Some(did) = imp.for_.def_id(self.cache) {
|
||||||
|
|
20
src/test/rustdoc/empty-impl-block.rs
Normal file
20
src/test/rustdoc/empty-impl-block.rs
Normal file
|
@ -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 {}
|
Loading…
Add table
Add a link
Reference in a new issue