summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-09 20:23:00 +0000
committerbors <bors@rust-lang.org>2023-02-09 20:23:00 +0000
commit8996ea93b6e554148c4286e62b613f12a3ee505c (patch)
tree1e54719a1d3b2d503d76ad2c7f4bc77d6c51ff45
parent8cca42a47f5d574c8f7302c98c3f918cdc772fbb (diff)
parentb080a1a4fc68bc6150a281f21012b79b9f1ee57f (diff)
downloadrust-8996ea93b6e554148c4286e62b613f12a3ee505c.tar.gz
Auto merge of #107853 - Dylan-DPC:rollup-macf1qo, r=Dylan-DPC
Rollup of 6 pull requests Successful merges: - #107648 (unused-lifetimes: don't warn about lifetimes originating from expanded code) - #107655 (rustdoc: use the same URL escape rules for fragments as for examples) - #107659 (test: snapshot for derive suggestion in diff files) - #107786 (Implement some tweaks in the new solver) - #107803 (Do not bring trait alias supertraits into scope) - #107815 (Disqualify `auto trait` built-in impl in new solver if explicit `impl` exists) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_errors/src/emitter.rs22
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs48
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs28
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly.rs81
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs60
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs85
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs8
-rw-r--r--library/core/src/result.rs6
-rw-r--r--src/librustdoc/html/markdown.rs43
-rw-r--r--src/librustdoc/html/render/mod.rs75
-rw-r--r--tests/rustdoc/const-generics/const-generics-docs.rs6
-rw-r--r--tests/rustdoc/const-generics/const-impl.rs10
-rw-r--r--tests/rustdoc/double-quote-escape.rs2
-rw-r--r--tests/rustdoc/primitive-tuple-variadic.rs4
-rw-r--r--tests/rustdoc/sidebar-links-to-foreign-impl.rs4
-rw-r--r--tests/rustdoc/where-clause-order.rs2
-rw-r--r--tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs12
-rw-r--r--tests/ui/modules/issue-107649.rs106
-rw-r--r--tests/ui/modules/issue-107649.stderr18
-rw-r--r--tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs21
-rw-r--r--tests/ui/traits/new-solver/elaborate-item-bounds.rs12
-rw-r--r--tests/ui/traits/new-solver/provisional-result-done.rs4
-rw-r--r--tests/ui/traits/new-solver/provisional-result-done.stderr15
-rw-r--r--tests/ui/traits/new-solver/temporary-ambiguity.rs22
-rw-r--r--tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs8
25 files changed, 437 insertions, 265 deletions
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 9768526a2f4..5f460b26488 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -1796,17 +1796,17 @@ impl EmitterWriter {
// telling users to make a change but not clarifying *where*.
let loc = sm.lookup_char_pos(parts[0].span.lo());
if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() {
- buffer.puts(row_num - 1, 0, "--> ", Style::LineNumber);
- buffer.append(
- row_num - 1,
- &format!(
- "{}:{}:{}",
- sm.filename_for_diagnostics(&loc.file.name),
- sm.doctest_offset_line(&loc.file.name, loc.line),
- loc.col.0 + 1,
- ),
- Style::LineAndColumn,
- );
+ let arrow = "--> ";
+ buffer.puts(row_num - 1, 0, arrow, Style::LineNumber);
+ let filename = sm.filename_for_diagnostics(&loc.file.name);
+ let offset = sm.doctest_offset_line(&loc.file.name, loc.line);
+ let message = format!("{}:{}:{}", filename, offset, loc.col.0 + 1);
+ if row_num == 2 {
+ let col = usize::max(max_line_num_len + 1, arrow.len());
+ buffer.puts(1, col, &message, Style::LineAndColumn);
+ } else {
+ buffer.append(row_num - 1, &message, Style::LineAndColumn);
+ }
for _ in 0..max_line_num_len {
buffer.prepend(row_num - 1, " ", Style::NoStyle);
}
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 4ce401b52bd..0cf58179ec2 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -951,24 +951,38 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs);
if self.tcx.is_trait_alias(trait_def_id) {
- // For trait aliases, assume all supertraits are relevant.
- let bounds = iter::once(ty::Binder::dummy(trait_ref));
- self.elaborate_bounds(bounds, |this, new_trait_ref, item| {
- let new_trait_ref = this.erase_late_bound_regions(new_trait_ref);
+ // For trait aliases, recursively assume all explicitly named traits are relevant
+ for expansion in traits::expand_trait_aliases(
+ self.tcx,
+ iter::once((ty::Binder::dummy(trait_ref), self.span)),
+ ) {
+ let bound_trait_ref = expansion.trait_ref();
+ for item in self.impl_or_trait_item(bound_trait_ref.def_id()) {
+ if !self.has_applicable_self(&item) {
+ self.record_static_candidate(CandidateSource::Trait(
+ bound_trait_ref.def_id(),
+ ));
+ } else {
+ let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref);
- let (xform_self_ty, xform_ret_ty) =
- this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs);
- this.push_candidate(
- Candidate {
- xform_self_ty,
- xform_ret_ty,
- item,
- import_ids: import_ids.clone(),
- kind: TraitCandidate(new_trait_ref),
- },
- false,
- );
- });
+ let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(
+ &item,
+ new_trait_ref.self_ty(),
+ new_trait_ref.substs,
+ );
+ self.push_candidate(
+ Candidate {
+ xform_self_ty,
+ xform_ret_ty,
+ item,
+ import_ids: import_ids.clone(),
+ kind: TraitCandidate(new_trait_ref),
+ },
+ false,
+ );
+ }
+ }
+ }
} else {
debug_assert!(self.tcx.is_trait(trait_def_id));
if self.tcx.trait_is_auto(trait_def_id) {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index cee0a7f3c20..a9dbb3ca131 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -2244,19 +2244,23 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
}
None => {
debug!(?param.ident, ?param.ident.span);
-
let deletion_span = deletion_span();
- self.r.lint_buffer.buffer_lint_with_diagnostic(
- lint::builtin::UNUSED_LIFETIMES,
- param.id,
- param.ident.span,
- &format!("lifetime parameter `{}` never used", param.ident),
- lint::BuiltinLintDiagnostics::SingleUseLifetime {
- param_span: param.ident.span,
- use_span: None,
- deletion_span,
- },
- );
+ // the give lifetime originates from expanded code so we won't be able to remove it #104432
+ let lifetime_only_in_expanded_code =
+ deletion_span.map(|sp| sp.in_derive_expansion()).unwrap_or(true);
+ if !lifetime_only_in_expanded_code {
+ self.r.lint_buffer.buffer_lint_with_diagnostic(
+ lint::builtin::UNUSED_LIFETIMES,
+ param.id,
+ param.ident.span,
+ &format!("lifetime parameter `{}` never used", param.ident),
+ lint::BuiltinLintDiagnostics::SingleUseLifetime {
+ param_span: param.ident.span,
+ use_span: None,
+ deletion_span,
+ },
+ );
+ }
}
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs
index 8525b96c0c2..775974d8e9a 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly.rs
@@ -3,7 +3,7 @@
use super::infcx_ext::InferCtxtExt;
#[cfg(doc)]
use super::trait_goals::structural_traits::*;
-use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
+use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate_predicates;
@@ -399,10 +399,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Alias(_, alias_ty) => alias_ty,
};
- for (assumption, _) in self
- .tcx()
- .bound_explicit_item_bounds(alias_ty.def_id)
- .subst_iter_copied(self.tcx(), alias_ty.substs)
+ for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs)
{
match G::consider_assumption(self, goal, assumption) {
Ok(result) => {
@@ -462,4 +459,78 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
}
+
+ #[instrument(level = "debug", skip(self), ret)]
+ pub(super) fn merge_candidates_and_discard_reservation_impls(
+ &mut self,
+ mut candidates: Vec<Candidate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ match candidates.len() {
+ 0 => return Err(NoSolution),
+ 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
+ _ => {}
+ }
+
+ if candidates.len() > 1 {
+ let mut i = 0;
+ 'outer: while i < candidates.len() {
+ for j in (0..candidates.len()).filter(|&j| i != j) {
+ if self.trait_candidate_should_be_dropped_in_favor_of(
+ &candidates[i],
+ &candidates[j],
+ ) {
+ debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
+ candidates.swap_remove(i);
+ continue 'outer;
+ }
+ }
+
+ debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
+ i += 1;
+ }
+
+ // If there are *STILL* multiple candidates, give up
+ // and report ambiguity.
+ if candidates.len() > 1 {
+ let certainty = if candidates.iter().all(|x| {
+ matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow))
+ }) {
+ Certainty::Maybe(MaybeCause::Overflow)
+ } else {
+ Certainty::AMBIGUOUS
+ };
+ return self.make_canonical_response(certainty);
+ }
+ }
+
+ Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
+ }
+
+ fn trait_candidate_should_be_dropped_in_favor_of(
+ &self,
+ candidate: &Candidate<'tcx>,
+ other: &Candidate<'tcx>,
+ ) -> bool {
+ // FIXME: implement this
+ match (candidate.source, other.source) {
+ (CandidateSource::Impl(_), _)
+ | (CandidateSource::ParamEnv(_), _)
+ | (CandidateSource::AliasBound, _)
+ | (CandidateSource::BuiltinImpl, _) => false,
+ }
+ }
+
+ fn discard_reservation_impl(&self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
+ if let CandidateSource::Impl(def_id) = candidate.source {
+ if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
+ debug!("Selected reservation impl");
+ // We assemble all candidates inside of a probe so by
+ // making a new canonical response here our result will
+ // have no constraints.
+ candidate.result = self.make_canonical_response(Certainty::AMBIGUOUS).unwrap();
+ }
+ }
+
+ candidate
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index e3ec71d1b4f..4fea49893a6 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -1,6 +1,6 @@
use crate::traits::{specialization_graph, translate_substs};
-use super::assembly::{self, Candidate, CandidateSource};
+use super::assembly;
use super::infcx_ext::InferCtxtExt;
use super::trait_goals::structural_traits;
use super::{Certainty, EvalCtxt, Goal, QueryResult};
@@ -34,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// projection cache in the solver.
if self.term_is_fully_unconstrained(goal) {
let candidates = self.assemble_and_evaluate_candidates(goal);
- self.merge_project_candidates(candidates)
+ self.merge_candidates_and_discard_reservation_impls(candidates)
} else {
let predicate = goal.predicate;
let unconstrained_rhs = match predicate.term.unpack() {
@@ -153,59 +153,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty))
}
-
- fn merge_project_candidates(
- &mut self,
- mut candidates: Vec<Candidate<'tcx>>,
- ) -> QueryResult<'tcx> {
- match candidates.len() {
- 0 => return Err(NoSolution),
- 1 => return Ok(candidates.pop().unwrap().result),
- _ => {}
- }
-
- if candidates.len() > 1 {
- let mut i = 0;
- 'outer: while i < candidates.len() {
- for j in (0..candidates.len()).filter(|&j| i != j) {
- if self.project_candidate_should_be_dropped_in_favor_of(
- &candidates[i],
- &candidates[j],
- ) {
- debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
- candidates.swap_remove(i);
- continue 'outer;
- }
- }
-
- debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
- // If there are *STILL* multiple candidates, give up
- // and report ambiguity.
- i += 1;
- if i > 1 {
- debug!("multiple matches, ambig");
- // FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
- unimplemented!();
- }
- }
- }
-
- Ok(candidates.pop().unwrap().result)
- }
-
- fn project_candidate_should_be_dropped_in_favor_of(
- &self,
- candidate: &Candidate<'tcx>,
- other: &Candidate<'tcx>,
- ) -> bool {
- // FIXME: implement this
- match (candidate.source, other.source) {
- (CandidateSource::Impl(_), _)
- | (CandidateSource::ParamEnv(_), _)
- | (CandidateSource::BuiltinImpl, _)
- | (CandidateSource::AliasBound, _) => unimplemented!(),
- }
- }
}
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
@@ -452,7 +399,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
[ty::GenericArg::from(goal.predicate.self_ty())],
));
- let is_sized_certainty = ecx.evaluate_goal(goal.with(tcx, sized_predicate))?.1;
+ let (_, is_sized_certainty) =
+ ecx.evaluate_goal(goal.with(tcx, sized_predicate))?;
return ecx.eq_term_and_make_canonical_response(
goal,
is_sized_certainty,
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 06a72e95d49..6554c739b3f 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -2,7 +2,7 @@
use std::iter;
-use super::assembly::{self, Candidate, CandidateSource};
+use super::assembly;
use super::infcx_ext::InferCtxtExt;
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
use rustc_hir::def_id::DefId;
@@ -89,6 +89,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ // This differs from the current stable behavior and
+ // fixes #84857. Due to breakage found via crater, we
+ // currently instead lint patterns which can be used to
+ // exploit this unsoundness on stable, see #93367 for
+ // more details.
+ if let Some(def_id) = ecx.tcx().find_map_relevant_impl(
+ goal.predicate.def_id(),
+ goal.predicate.self_ty(),
+ Some,
+ ) {
+ debug!(?def_id, ?goal, "disqualified auto-trait implementation");
+ return Err(NoSolution);
+ }
+
ecx.probe_and_evaluate_goal_for_constituent_tys(
goal,
structural_traits::instantiate_constituent_tys_for_auto_trait,
@@ -479,73 +493,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, TraitPredicate<'tcx>>,
) -> QueryResult<'tcx> {
let candidates = self.assemble_and_evaluate_candidates(goal);
- self.merge_trait_candidates_discard_reservation_impls(candidates)
- }
-
- #[instrument(level = "debug", skip(self), ret)]
- pub(super) fn merge_trait_candidates_discard_reservation_impls(
- &mut self,
- mut candidates: Vec<Candidate<'tcx>>,
- ) -> QueryResult<'tcx> {
- match candidates.len() {
- 0 => return Err(NoSolution),
- 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
- _ => {}
- }
-
- if candidates.len() > 1 {
- let mut i = 0;
- 'outer: while i < candidates.len() {
- for j in (0..candidates.len()).filter(|&j| i != j) {
- if self.trait_candidate_should_be_dropped_in_favor_of(
- &candidates[i],
- &candidates[j],
- ) {
- debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
- candidates.swap_remove(i);
- continue 'outer;
- }
- }
-
- debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
- // If there are *STILL* multiple candidates, give up
- // and report ambiguity.
- i += 1;
- if i > 1 {
- debug!("multiple matches, ambig");
- // FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
- unimplemented!();
- }
- }
- }
-
- Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
- }
-
- fn trait_candidate_should_be_dropped_in_favor_of(
- &self,
- candidate: &Candidate<'tcx>,
- other: &Candidate<'tcx>,
- ) -> bool {
- // FIXME: implement this
- match (candidate.source, other.source) {
- (CandidateSource::Impl(_), _)
- | (CandidateSource::ParamEnv(_), _)
- | (CandidateSource::AliasBound, _)
- | (CandidateSource::BuiltinImpl, _) => unimplemented!(),
- }
- }
-
- fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> {
- if let CandidateSource::Impl(def_id) = candidate.source {
- if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
- debug!("Selected reservation impl");
- // FIXME: reduce candidate to ambiguous
- // FIXME: replace `var_values` with identity, yeet external constraints.
- unimplemented!()
- }
- }
-
- candidate
+ self.merge_candidates_and_discard_reservation_impls(candidates)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 8f548acfd2e..bafa2981a87 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -646,11 +646,9 @@ fn object_ty_for_trait<'tcx>(
debug!(?obligation);
let pred = obligation.predicate.to_opt_poly_projection_pred()?;
Some(pred.map_bound(|p| {
- ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
- def_id: p.projection_ty.def_id,
- substs: p.projection_ty.substs,
- term: p.term,
- })
+ ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
+ tcx, p,
+ ))
}))
})
.collect();
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index f00c40f35d5..7596e9cc005 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -458,7 +458,7 @@
//! [`Result`] of a collection of each contained value of the original
//! [`Result`] values, or [`Err`] if any of the elements was [`Err`].
//!
-//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA%2C%20E%3E%3E-for-Result%3CV%2C%20E%3E
+//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA,+E%3E%3E-for-Result%3CV,+E%3E
//!
//! ```
//! let v = [Ok(2), Ok(4), Err("err!"), Ok(8)];
@@ -474,8 +474,8 @@
//! to provide the [`product`][Iterator::product] and
//! [`sum`][Iterator::sum] methods.
//!
-//! [impl-Product]: Result#impl-Product%3CResult%3CU%2C%20E%3E%3E-for-Result%3CT%2C%20E%3E
-//! [impl-Sum]: Result#impl-Sum%3CResult%3CU%2C%20E%3E%3E-for-Result%3CT%2C%20E%3E
+//! [impl-Product]: Result#impl-Product%3CResult%3CU,+E%3E%3E-for-Result%3CT,+E%3E
+//! [impl-Sum]: Result#impl-Sum%3CResult%3CU,+E%3E%3E-for-Result%3CT,+E%3E
//!
//! ```
//! let v = [Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")];
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index fb7c34118a4..94de93e7a99 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -46,6 +46,7 @@ use crate::html::escape::Escape;
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::length_limit::HtmlWithLimit;
+use crate::html::render::small_url_encode;
use crate::html::toc::TocBuilder;
use pulldown_cmark::{
@@ -294,47 +295,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
doctest::make_test(&test, krate, false, &Default::default(), edition, None);
let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
- // These characters don't need to be escaped in a URI.
- // See https://url.spec.whatwg.org/#query-percent-encode-set
- // and https://url.spec.whatwg.org/#urlencoded-parsing
- // and https://url.spec.whatwg.org/#url-code-points
- fn dont_escape(c: u8) -> bool {
- (b'a' <= c && c <= b'z')
- || (b'A' <= c && c <= b'Z')
- || (b'0' <= c && c <= b'9')
- || c == b'-'
- || c == b'_'
- || c == b'.'
- || c == b','
- || c == b'~'
- || c == b'!'
- || c == b'\''
- || c == b'('
- || c == b')'
- || c == b'*'
- || c == b'/'
- || c == b';'
- || c == b':'
- || c == b'?'
- // As described in urlencoded-parsing, the
- // first `=` is the one that separates key from
- // value. Following `=`s are part of the value.
- || c == b'='
- }
- let mut test_escaped = String::new();
- for b in test.bytes() {
- if dont_escape(b) {
- test_escaped.push(char::from(b));
- } else if b == b' ' {
- // URL queries are decoded with + replaced with SP
- test_escaped.push('+');
- } else if b == b'%' {
- test_escaped.push('%');
- test_escaped.push('%');
- } else {
- write!(test_escaped, "%{:02X}", b).unwrap();
- }
- }
+ let test_escaped = small_url_encode(test);
Some(format!(
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}&amp;edition={}">Run</a>"#,
url, test_escaped, channel, edition,
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 816a8f4e274..fa22c461205 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -38,7 +38,7 @@ pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc};
use std::collections::VecDeque;
use std::default::Default;
-use std::fmt;
+use std::fmt::{self, Write};
use std::fs;
use std::iter::Peekable;
use std::path::PathBuf;
@@ -2020,31 +2020,60 @@ fn get_associated_constants(
.collect::<Vec<_>>()
}
-// The point is to url encode any potential character from a type with genericity.
-fn small_url_encode(s: String) -> String {
+pub(crate) fn small_url_encode(s: String) -> String {
+ // These characters don't need to be escaped in a URI.
+ // See https://url.spec.whatwg.org/#query-percent-encode-set
+ // and https://url.spec.whatwg.org/#urlencoded-parsing
+ // and https://url.spec.whatwg.org/#url-code-points
+ fn dont_escape(c: u8) -> bool {
+ (b'a' <= c && c <= b'z')
+ || (b'A' <= c && c <= b'Z')
+ || (b'0' <= c && c <= b'9')
+ || c == b'-'
+ || c == b'_'
+ || c == b'.'
+ || c == b','
+ || c == b'~'
+ || c == b'!'
+ || c == b'\''
+ || c == b'('
+ || c == b')'
+ || c == b'*'
+ || c == b'/'
+ || c == b';'
+ || c == b':'
+ || c == b'?'
+ // As described in urlencoded-parsing, the
+ // first `=` is the one that separates key from
+ // value. Following `=`s are part of the value.
+ || c == b'='
+ }
let mut st = String::new();
let mut last_match = 0;
- for (idx, c) in s.char_indices() {
- let escaped = match c {
- '<' => "%3C",
- '>' => "%3E",
- ' ' => "%20",
- '?' => "%3F",
- '\'' => "%27",
- '&' => "%26",
- ',' => "%2C",
- ':' => "%3A",
- ';' => "%3B",
- '[' => "%5B",
- ']' => "%5D",
- '"' => "%22",
- _ => continue,
- };
+ for (idx, b) in s.bytes().enumerate() {
+ if dont_escape(b) {
+ continue;
+ }
- st += &s[last_match..idx];
- st += escaped;
- // NOTE: we only expect single byte characters here - which is fine as long as we
- // only match single byte characters
+ if last_match != idx {
+ // Invariant: `idx` must be the first byte in a character at this point.
+ st += &s[last_match..idx];
+ }
+ if b == b' ' {
+ // URL queries are decoded with + replaced with SP.
+ // While the same is not true for hashes, rustdoc only needs to be
+ // consistent with itself when encoding them.
+ st += "+";
+ } else if b == b'%' {
+ st += "%%";
+ } else {
+ write!(st, "%{:02X}", b).unwrap();
+ }
+ // Invariant: if the current byte is not at the start of a multi-byte character,
+ // we need to get down here so that when the next turn of the loop comes around,
+ // last_match winds up equalling idx.
+ //
+ // In other words, dont_escape must always return `false` in multi-byte character.
last_match = idx + 1;
}
diff --git a/tests/rustdoc/const-generics/const-generics-docs.rs b/tests/rustdoc/const-generics/const-generics-docs.rs
index cbda095424b..828486a41d4 100644
--- a/tests/rustdoc/const-generics/const-generics-docs.rs
+++ b/tests/rustdoc/const-generics/const-generics-docs.rs
@@ -21,8 +21,8 @@ pub use extern_crate::WTrait;
// 'pub trait Trait<const N: usize>'
// @has - '//*[@id="impl-Trait%3C1%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<1> for u8'
// @has - '//*[@id="impl-Trait%3C2%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<2> for u8'
-// @has - '//*[@id="impl-Trait%3C{1%20+%202}%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<{1 + 2}> for u8'
-// @has - '//*[@id="impl-Trait%3CN%3E-for-%5Bu8%3B%20N%5D"]//h3[@class="code-header"]' \
+// @has - '//*[@id="impl-Trait%3C%7B1+%2B+2%7D%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<{1 + 2}> for u8'
+// @has - '//*[@id="impl-Trait%3CN%3E-for-%5Bu8;+N%5D"]//h3[@class="code-header"]' \
// 'impl<const N: usize> Trait<N> for [u8; N]'
pub trait Trait<const N: usize> {}
impl Trait<1> for u8 {}
@@ -47,7 +47,7 @@ impl<const M: usize> Foo<M> where u8: Trait<M> {
}
}
-// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8%2C%20M%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Bar<u8, M>'
+// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8,+M%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Bar<u8, M>'
impl<const M: usize> Bar<u8, M> {
// @has - '//*[@id="method.hey"]' \
// 'pub fn hey<const N: usize>(&self) -> Foo<N>where u8: Trait<N>'
diff --git a/tests/rustdoc/const-generics/const-impl.rs b/tests/rustdoc/const-generics/const-impl.rs
index 91866b7d890..152b643bf4b 100644
--- a/tests/rustdoc/const-generics/const-impl.rs
+++ b/tests/rustdoc/const-generics/const-impl.rs
@@ -9,20 +9,20 @@ pub enum Order {
}
// @has foo/struct.VSet.html '//pre[@class="rust item-decl"]' 'pub struct VSet<T, const ORDER: Order>'
-// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>'
-// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>'
+// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT,+ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>'
+// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT,+ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>'
pub struct VSet<T, const ORDER: Order> {
inner: Vec<T>,
}
-// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT%2C%20{%20Order%3A%3ASorted%20}%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Sorted }>'
+// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT,+%7B+Order::Sorted+%7D%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Sorted }>'
impl<T> VSet<T, { Order::Sorted }> {
pub fn new() -> Self {
Self { inner: Vec::new() }
}
}
-// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT%2C%20{%20Order%3A%3AUnsorted%20}%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Unsorted }>'
+// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT,+%7B+Order::Unsorted+%7D%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Unsorted }>'
impl<T> VSet<T, { Order::Unsorted }> {
pub fn new() -> Self {
Self { inner: Vec::new() }
@@ -31,7 +31,7 @@ impl<T> VSet<T, { Order::Unsorted }> {
pub struct Escape<const S: &'static str>;
-// @has foo/struct.Escape.html '//*[@id="impl-Escape%3Cr#%22%3Cscript%3Ealert(%22Escape%22)%3B%3C/script%3E%22#%3E"]/h3[@class="code-header"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>'
+// @has foo/struct.Escape.html '//*[@id="impl-Escape%3Cr%23%22%3Cscript%3Ealert(%22Escape%22);%3C/script%3E%22%23%3E"]/h3[@class="code-header"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>'
impl Escape<r#"<script>alert("Escape");</script>"#> {
pub fn f() {}
}
diff --git a/tests/rustdoc/double-quote-escape.rs b/tests/rustdoc/double-quote-escape.rs
index 350c897417d..4f4436377a0 100644
--- a/tests/rustdoc/double-quote-escape.rs
+++ b/tests/rustdoc/double-quote-escape.rs
@@ -7,5 +7,5 @@ pub trait Foo<T> {
pub struct Bar;
// @has foo/struct.Bar.html
-// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe%20extern%20%22C%22%20fn()%3E-for-Bar"]' 'Foo<unsafe extern "C" fn()>'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe+extern+%22C%22+fn()%3E-for-Bar"]' 'Foo<unsafe extern "C" fn()>'
impl Foo<unsafe extern "C" fn()> for Bar {}
diff --git a/tests/rustdoc/primitive-tuple-variadic.rs b/tests/rustdoc/primitive-tuple-variadic.rs
index db7cfd60c71..846028bbb19 100644
--- a/tests/rustdoc/primitive-tuple-variadic.rs
+++ b/tests/rustdoc/primitive-tuple-variadic.rs
@@ -6,13 +6,13 @@
pub trait Foo {}
// @has foo/trait.Foo.html
-// @has - '//section[@id="impl-Foo-for-(T%2C)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)'
+// @has - '//section[@id="impl-Foo-for-(T,)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)'
#[doc(fake_variadic)]
impl<T> Foo for (T,) {}
pub trait Bar {}
// @has foo/trait.Bar.html
-// @has - '//section[@id="impl-Bar-for-(U%2C)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)'
+// @has - '//section[@id="impl-Bar-for-(U,)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)'
#[doc(fake_variadic)]
impl<U: Foo> Bar for (U,) {}
diff --git a/tests/rustdoc/sidebar-links-to-foreign-impl.rs b/tests/rustdoc/sidebar-links-to-foreign-impl.rs
index 11e94694802..caa17dfbb1c 100644
--- a/tests/rustdoc/sidebar-links-to-foreign-impl.rs
+++ b/tests/rustdoc/sidebar-links-to-foreign-impl.rs
@@ -7,8 +7,8 @@
// @has - '//h2[@id="foreign-impls"]' 'Implementations on Foreign Types'
// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-u32"]' 'u32'
// @has - '//*[@id="impl-Foo-for-u32"]//h3[@class="code-header"]' 'impl Foo for u32'
-// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-%26%27a%20str"]' "&'a str"
-// @has - '//*[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header"]' "impl<'a> Foo for &'a str"
+// @has - "//*[@class=\"sidebar-elems\"]//section//a[@href=\"#impl-Foo-for-%26'a+str\"]" "&'a str"
+// @has - "//*[@id=\"impl-Foo-for-%26'a+str\"]//h3[@class=\"code-header\"]" "impl<'a> Foo for &'a str"
pub trait Foo {}
impl Foo for u32 {}
diff --git a/tests/rustdoc/where-clause-order.rs b/tests/rustdoc/where-clause-order.rs
index b8502e10a48..b10f8f6856e 100644
--- a/tests/rustdoc/where-clause-order.rs
+++ b/tests/rustdoc/where-clause-order.rs
@@ -7,7 +7,7 @@ where
}
// @has 'foo/trait.SomeTrait.html'
-// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
+// @has - "//*[@id='impl-SomeTrait%3C(A,+B,+C,+D,+E)%3E-for-(A,+B,+C,+D,+E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)
where
A: PartialOrd<A> + PartialEq<A>,
diff --git a/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs b/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs
new file mode 100644
index 00000000000..5d5429ec895
--- /dev/null
+++ b/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs
@@ -0,0 +1,12 @@
+// check-pass
+
+#![deny(unused_lifetimes)]
+trait Trait2 {
+ type As;
+}
+
+// we should not warn about an unused lifetime about code generated from this proc macro here
+#[derive(Clone)]
+struct ShimMethod4<T: Trait2 + 'static>(pub &'static dyn for<'s> Fn(&'s mut T::As));
+
+pub fn main() {}
diff --git a/tests/ui/modules/issue-107649.rs b/tests/ui/modules/issue-107649.rs
new file mode 100644
index 00000000000..71b84cd30d6
--- /dev/null
+++ b/tests/ui/modules/issue-107649.rs
@@ -0,0 +1,106 @@
+// compile-flags: -Z ui-testing=no
+#[path = "auxiliary/dummy_lib.rs"]
+mod lib;
+
+/// The function needs to be long enough to
+/// ensure `max_line_num_len` to be large enough
+/// for no-ui-testing
+fn main() {
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ ();
+ dbg!(lib::Dummy); //~ Error: `Dummy` doesn't implement `Debug`
+}
diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr
new file mode 100644
index 00000000000..1cea71f2829
--- /dev/null
+++ b/tests/ui/modules/issue-107649.stderr
@@ -0,0 +1,18 @@
+error[E0277]: `Dummy` doesn't implement `Debug`
+ --> $DIR/issue-107649.rs:105:5
+ |
+105 | dbg!(lib::Dummy);
+ | ^^^^^^^^^^^^^^^^ `Dummy` cannot be formatted using `{:?}`
+ |
+ = help: the trait `Debug` is not implemented for `Dummy`
+ = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy`
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider annotating `Dummy` with `#[derive(Debug)]`
+ --> $DIR/auxiliary/dummy_lib.rs:2:1
+ |
+2 | #[derive(Debug)]
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs b/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs
new file mode 100644
index 00000000000..9b41a8096c4
--- /dev/null
+++ b/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs
@@ -0,0 +1,21 @@
+// Regression test for #107747: methods from trait alias supertraits were brought into scope
+//
+// check-pass
+
+#![feature(trait_alias)]
+
+use std::fmt;
+
+trait Foo: fmt::Debug {}
+trait Bar = Foo;
+
+#[derive(Debug)]
+struct Qux(bool);
+
+impl fmt::Display for Qux {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/elaborate-item-bounds.rs b/tests/ui/traits/new-solver/elaborate-item-bounds.rs
new file mode 100644
index 00000000000..076aefcf8fc
--- /dev/null
+++ b/tests/ui/traits/new-solver/elaborate-item-bounds.rs
@@ -0,0 +1,12 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+trait Foo {
+ type Bar: Bar;
+}
+
+trait Bar: Baz {}
+
+trait Baz {}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/provisional-result-done.rs b/tests/ui/traits/new-solver/provisional-result-done.rs
index a3d97927bad..254ab356ad8 100644
--- a/tests/ui/traits/new-solver/provisional-result-done.rs
+++ b/tests/ui/traits/new-solver/provisional-result-done.rs
@@ -1,9 +1,5 @@
// known-bug: unknown
// compile-flags: -Ztrait-solver=next
-// failure-status: 101
-// normalize-stderr-test "note: .*\n\n" -> ""
-// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
-// rustc-env:RUST_BACKTRACE=0
// This tests checks that we update results in the provisional cache when
// we pop a goal from the stack.
diff --git a/tests/ui/traits/new-solver/provisional-result-done.stderr b/tests/ui/traits/new-solver/provisional-result-done.stderr
index ffc92b81f08..5bd0613d259 100644
--- a/tests/ui/traits/new-solver/provisional-result-done.stderr
+++ b/tests/ui/traits/new-solver/provisional-result-done.stderr
@@ -1,6 +1,11 @@
-error: the compiler unexpectedly panicked. this is a bug.
+error[E0283]: type annotations needed: cannot satisfy `Bar<T>: Coinductive`
+ --> $DIR/provisional-result-done.rs:16:25
+ |
+LL | impl<T> Coinductive for Bar<T>
+ | ^^^^^^
+ |
+ = note: cannot satisfy `Bar<T>: Coinductive`
-query stack during panic:
-#0 [check_well_formed] checking that `<impl at $DIR/provisional-result-done.rs:20:1: 20:31>` is well-formed
-#1 [check_mod_type_wf] checking that types are well-formed in top-level module
-end of query stack
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/new-solver/temporary-ambiguity.rs b/tests/ui/traits/new-solver/temporary-ambiguity.rs
new file mode 100644
index 00000000000..18ee0545700
--- /dev/null
+++ b/tests/ui/traits/new-solver/temporary-ambiguity.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+// Checks that we don't explode when we assemble >1 candidate for a goal.
+
+struct Wrapper<T>(T);
+
+trait Foo {}
+
+impl Foo for Wrapper<i32> {}
+
+impl Foo for Wrapper<()> {}
+
+fn needs_foo(_: impl Foo) {}
+
+fn main() {
+ let mut x = Default::default();
+ let w = Wrapper(x);
+ needs_foo(w);
+ x = 1;
+ drop(x);
+}
diff --git a/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs b/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs
new file mode 100644
index 00000000000..bcfc747ebb1
--- /dev/null
+++ b/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs
@@ -0,0 +1,8 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+struct Foo(*mut ());
+
+unsafe impl Sync for Foo {}
+
+fn main() {}