diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 122 | ||||
| -rw-r--r-- | src/librustdoc/html/static/css/rustdoc.css | 4 | ||||
| -rw-r--r-- | src/librustdoc/passes/html_tags.rs | 55 | ||||
| -rw-r--r-- | src/test/rustdoc-ui/invalid-html-self-closing-tag.rs | 70 | ||||
| -rw-r--r-- | src/test/rustdoc-ui/invalid-html-self-closing-tag.stderr | 80 | ||||
| -rw-r--r-- | src/test/rustdoc/async-trait.rs | 16 | ||||
| -rw-r--r-- | src/test/rustdoc/auxiliary/async-trait-dep.rs | 9 | ||||
| -rw-r--r-- | src/test/ui/async-await/async-fn-nonsend.stderr | 2 |
8 files changed, 300 insertions, 58 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d86a2682641..a3a1abc3cf0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -415,6 +415,16 @@ fn clean_projection<'tcx>( cx: &mut DocContext<'tcx>, def_id: Option<DefId>, ) -> Type { + if cx.tcx.def_kind(ty.item_def_id) == DefKind::ImplTraitPlaceholder { + let bounds = cx + .tcx + .explicit_item_bounds(ty.item_def_id) + .iter() + .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.substs)) + .collect::<Vec<_>>(); + return clean_middle_opaque_bounds(cx, bounds); + } + let trait_ = clean_trait_ref_with_bindings(cx, ty.trait_ref(cx.tcx), ThinVec::new()); let self_type = clean_middle_ty(ty.self_ty(), cx, None); let self_def_id = if let Some(def_id) = def_id { @@ -1720,59 +1730,7 @@ pub(crate) fn clean_middle_ty<'tcx>( .iter() .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, substs)) .collect::<Vec<_>>(); - let mut regions = vec![]; - let mut has_sized = false; - let mut bounds = bounds - .iter() - .filter_map(|bound| { - let bound_predicate = bound.kind(); - let trait_ref = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - if let Some(r) = clean_middle_region(reg) { - regions.push(GenericBound::Outlives(r)); - } - return None; - } - _ => return None, - }; - - if let Some(sized) = cx.tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized { - has_sized = true; - return None; - } - } - - let bindings: ThinVec<_> = bounds - .iter() - .filter_map(|bound| { - if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() - { - if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { - Some(TypeBinding { - assoc: projection_to_path_segment(proj.projection_ty, cx), - kind: TypeBindingKind::Equality { - term: clean_middle_term(proj.term, cx), - }, - }) - } else { - None - } - } else { - None - } - }) - .collect(); - - Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) - }) - .collect::<Vec<_>>(); - bounds.extend(regions); - if !has_sized && !bounds.is_empty() { - bounds.insert(0, GenericBound::maybe_sized(cx)); - } - ImplTrait(bounds) + clean_middle_opaque_bounds(cx, bounds) } ty::Closure(..) => panic!("Closure"), @@ -1785,6 +1743,64 @@ pub(crate) fn clean_middle_ty<'tcx>( } } +fn clean_middle_opaque_bounds<'tcx>( + cx: &mut DocContext<'tcx>, + bounds: Vec<ty::Predicate<'tcx>>, +) -> Type { + let mut regions = vec![]; + let mut has_sized = false; + let mut bounds = bounds + .iter() + .filter_map(|bound| { + let bound_predicate = bound.kind(); + let trait_ref = match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + if let Some(r) = clean_middle_region(reg) { + regions.push(GenericBound::Outlives(r)); + } + return None; + } + _ => return None, + }; + + if let Some(sized) = cx.tcx.lang_items().sized_trait() { + if trait_ref.def_id() == sized { + has_sized = true; + return None; + } + } + + let bindings: ThinVec<_> = bounds + .iter() + .filter_map(|bound| { + if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() { + if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { + Some(TypeBinding { + assoc: projection_to_path_segment(proj.projection_ty, cx), + kind: TypeBindingKind::Equality { + term: clean_middle_term(proj.term, cx), + }, + }) + } else { + None + } + } else { + None + } + }) + .collect(); + + Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) + }) + .collect::<Vec<_>>(); + bounds.extend(regions); + if !has_sized && !bounds.is_empty() { + bounds.insert(0, GenericBound::maybe_sized(cx)); + } + ImplTrait(bounds) +} + pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let def_id = cx.tcx.hir().local_def_id(field.hir_id).to_def_id(); clean_field_with_def_id(def_id, field.ident.name, clean_ty(field.ty, cx), cx) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 5a1a6c0f0f9..934334e0c88 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -857,9 +857,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ causes rounded corners and no border on iOS Safari. */ -webkit-appearance: none; /* Override Normalize.css: we have margins and do - not want to overflow - the `moz` attribute is necessary - until Firefox 29, too early to drop at this point */ - -moz-box-sizing: border-box !important; + not want to overflow */ box-sizing: border-box !important; outline: none; border: 1px solid var(--border-color); diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs index 67fc71665cc..a89ed7c7ed4 100644 --- a/src/librustdoc/passes/html_tags.rs +++ b/src/librustdoc/passes/html_tags.rs @@ -184,7 +184,60 @@ fn extract_html_tag( } drop_tag(tags, tag_name, r, f); } else { - tags.push((tag_name, r)); + let mut is_self_closing = false; + let mut quote_pos = None; + if c != '>' { + let mut quote = None; + let mut after_eq = false; + for (i, c) in text[pos..].char_indices() { + if !c.is_whitespace() { + if let Some(q) = quote { + if c == q { + quote = None; + quote_pos = None; + after_eq = false; + } + } else if c == '>' { + break; + } else if c == '/' && !after_eq { + is_self_closing = true; + } else { + if is_self_closing { + is_self_closing = false; + } + if (c == '"' || c == '\'') && after_eq { + quote = Some(c); + quote_pos = Some(pos + i); + } else if c == '=' { + after_eq = true; + } + } + } else if quote.is_none() { + after_eq = false; + } + } + } + if let Some(quote_pos) = quote_pos { + let qr = Range { start: quote_pos, end: quote_pos }; + f( + &format!("unclosed quoted HTML attribute on tag `{}`", tag_name), + &qr, + false, + ); + } + if is_self_closing { + // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus + let valid = ALLOWED_UNCLOSED.contains(&&tag_name[..]) + || tags.iter().take(pos + 1).any(|(at, _)| { + let at = at.to_lowercase(); + at == "svg" || at == "math" + }); + if !valid { + f(&format!("invalid self-closing HTML tag `{}`", tag_name), &r, false); + } + } else { + tags.push((tag_name, r)); + } } } break; diff --git a/src/test/rustdoc-ui/invalid-html-self-closing-tag.rs b/src/test/rustdoc-ui/invalid-html-self-closing-tag.rs new file mode 100644 index 00000000000..d973a53cbc7 --- /dev/null +++ b/src/test/rustdoc-ui/invalid-html-self-closing-tag.rs @@ -0,0 +1,70 @@ +#![deny(rustdoc::invalid_html_tags)] + +/// <p/> +//~^ ERROR invalid self-closing HTML tag `p` +pub struct A; + +/// <p style/> +//~^ ERROR invalid self-closing HTML tag `p` +pub struct B; + +/// <p style=""/> +//~^ ERROR invalid self-closing HTML tag `p` +pub struct C; + +/// <p style="x"/> +//~^ ERROR invalid self-closing HTML tag `p` +pub struct D; + +/// <p style="x/></p> +//~^ ERROR unclosed quoted HTML attribute +pub struct E; + +/// <p style='x/></p> +//~^ ERROR unclosed quoted HTML attribute +pub struct F; + +/// <p style="x/"></p> +pub struct G; + +/// <p style="x/"/> +//~^ ERROR invalid self-closing HTML tag `p` +pub struct H; + +/// <p / > +//~^ ERROR invalid self-closing HTML tag `p` +pub struct I; + +/// <br/> +pub struct J; + +/// <a href=/></a> +pub struct K; + +/// <a href=//></a> +pub struct L; + +/// <a href="/"/> +//~^ ERROR invalid self-closing HTML tag `a` +pub struct M; + +/// <a href=x /> +//~^ ERROR invalid self-closing HTML tag `a` +pub struct N; + +/// <a href= /> +//~^ ERROR invalid self-closing HTML tag `a` +pub struct O; + +/// <a href=x/></a> +pub struct P; + +/// <svg><rect width=1 height=1 /></svg> +pub struct Q; + +/// <svg><rect width=1 height=/></svg> +//~^ ERROR unclosed HTML tag `rect` +pub struct R; + +/// <svg / q> +pub struct S; diff --git a/src/test/rustdoc-ui/invalid-html-self-closing-tag.stderr b/src/test/rustdoc-ui/invalid-html-self-closing-tag.stderr new file mode 100644 index 00000000000..e45edfb43ff --- /dev/null +++ b/src/test/rustdoc-ui/invalid-html-self-closing-tag.stderr @@ -0,0 +1,80 @@ +error: invalid self-closing HTML tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:3:5 + | +LL | /// <p/> + | ^^ + | +note: the lint level is defined here + --> $DIR/invalid-html-self-closing-tag.rs:1:9 + | +LL | #![deny(rustdoc::invalid_html_tags)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid self-closing HTML tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:7:5 + | +LL | /// <p style/> + | ^^ + +error: invalid self-closing HTML tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:11:5 + | +LL | /// <p style=""/> + | ^^ + +error: invalid self-closing HTML tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:15:5 + | +LL | /// <p style="x"/> + | ^^ + +error: unclosed quoted HTML attribute on tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:19:14 + | +LL | /// <p style="x/></p> + | ^ + +error: unclosed quoted HTML attribute on tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:23:14 + | +LL | /// <p style='x/></p> + | ^ + +error: invalid self-closing HTML tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:30:5 + | +LL | /// <p style="x/"/> + | ^^ + +error: invalid self-closing HTML tag `p` + --> $DIR/invalid-html-self-closing-tag.rs:34:5 + | +LL | /// <p / > + | ^^ + +error: invalid self-closing HTML tag `a` + --> $DIR/invalid-html-self-closing-tag.rs:47:5 + | +LL | /// <a href="/"/> + | ^^ + +error: invalid self-closing HTML tag `a` + --> $DIR/invalid-html-self-closing-tag.rs:51:5 + | +LL | /// <a href=x /> + | ^^ + +error: invalid self-closing HTML tag `a` + --> $DIR/invalid-html-self-closing-tag.rs:55:5 + | +LL | /// <a href= /> + | ^^ + +error: unclosed HTML tag `rect` + --> $DIR/invalid-html-self-closing-tag.rs:65:10 + | +LL | /// <svg><rect width=1 height=/></svg> + | ^^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/test/rustdoc/async-trait.rs b/src/test/rustdoc/async-trait.rs new file mode 100644 index 00000000000..a473e467473 --- /dev/null +++ b/src/test/rustdoc/async-trait.rs @@ -0,0 +1,16 @@ +// aux-build:async-trait-dep.rs +// edition:2021 + +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +extern crate async_trait_dep; + +pub struct Oink {} + +// @has 'async_trait/struct.Oink.html' '//h4[@class="code-header"]' "async fn woof()" +impl async_trait_dep::Meow for Oink { + async fn woof() { + todo!() + } +} diff --git a/src/test/rustdoc/auxiliary/async-trait-dep.rs b/src/test/rustdoc/auxiliary/async-trait-dep.rs new file mode 100644 index 00000000000..10a55dd0260 --- /dev/null +++ b/src/test/rustdoc/auxiliary/async-trait-dep.rs @@ -0,0 +1,9 @@ +// edition:2021 + +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +pub trait Meow { + /// Who's a good dog? + async fn woof(); +} diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr index 40ad46b4862..a7b872fe444 100644 --- a/src/test/ui/async-await/async-fn-nonsend.stderr +++ b/src/test/ui/async-await/async-fn-nonsend.stderr @@ -27,7 +27,7 @@ error: future cannot be sent between threads safely LL | assert_send(non_sync_with_method_call()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send` | - = help: the trait `Send` is not implemented for `dyn std::fmt::Write` + = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write` note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:46:14 | |
