Skip to content
Closed
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
10 changes: 8 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1771,8 +1771,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
let pat = self.lower_pat(pat);
let for_span =
self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None);
let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None);
let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None);
let for_ctxt = for_span.ctxt();

// Try to point both the head and pat spans to their position in the for loop
// rather than inside a macro.
let head_span =
head.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(head.span).with_ctxt(for_ctxt);
let pat_span =
pat.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(pat.span).with_ctxt(for_ctxt);

let loop_hir_id = self.lower_node_id(e.id);
let label = self.lower_label(opt_label, e.id, loop_hir_id);
Expand Down
66 changes: 66 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
self.suggest_bounds_for_range_to_method(&mut err, source, item_ident);
err.emit()
}

Expand Down Expand Up @@ -3260,6 +3261,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn suggest_bounds_for_range_to_method(
&self,
err: &mut Diag<'_>,
source: SelfSource<'tcx>,
item_ident: Ident,
) {
let SelfSource::MethodCall(rcvr_expr) = source else { return };
let hir::ExprKind::Struct(qpath, fields, _) = rcvr_expr.kind else { return };
let Some(lang_item) = self.tcx.qpath_lang_item(*qpath) else {
return;
};
let is_inclusive = match lang_item {
hir::LangItem::RangeTo => false,
hir::LangItem::RangeToInclusive | hir::LangItem::RangeInclusiveCopy => true,
_ => return,
};

let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) else { return };
let Some(_) = self
.tcx
.associated_items(iterator_trait)
.filter_by_name_unhygienic(item_ident.name)
.next()
else {
return;
};

let source_map = self.tcx.sess.source_map();
let range_type = if is_inclusive { "RangeInclusive" } else { "Range" };
let Some(end_field) = fields.iter().find(|f| f.ident.name == rustc_span::sym::end) else {
return;
};

let element_ty = self.typeck_results.borrow().expr_ty_opt(end_field.expr);
let is_integral = element_ty.is_some_and(|ty| ty.is_integral());
let end_is_negative = is_integral
&& matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _));

let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) else { return };

let offset = snippet
.chars()
.take_while(|&c| c == '(' || c.is_whitespace())
.map(|c| c.len_utf8())
.sum::<usize>();

let insert_span = rcvr_expr
.span
.with_lo(rcvr_expr.span.lo() + rustc_span::BytePos(offset as u32))
.shrink_to_lo();

let (value, appl) = if is_integral && !end_is_negative {
("0", Applicability::MachineApplicable)
} else {
("/* start */", Applicability::HasPlaceholders)
};

err.span_suggestion_verbose(
insert_span,
format!("consider using a bounded `{range_type}` by adding a concrete starting value"),
value,
appl,
);
}

/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
Expand Down
10 changes: 1 addition & 9 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3198,15 +3198,7 @@ impl Target {
return load_file(&p);
}

// Leave in a specialized error message for the removed target.
// FIXME: If you see this and it's been a few months after this has been released,
// you can probably remove it.
if target_tuple == "i586-pc-windows-msvc" {
Err("the `i586-pc-windows-msvc` target has been removed. Use the `i686-pc-windows-msvc` target instead.\n\
Windows 10 (the minimum required OS version) requires a CPU baseline of at least i686 so you can safely switch".into())
} else {
Err(format!("could not find specification for target {target_tuple:?}"))
}
Err(format!("could not find specification for target {target_tuple:?}"))
}
TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents),
}
Expand Down
75 changes: 46 additions & 29 deletions src/build_helper/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,13 @@ pub fn check_path_modifications(

/// Returns true if any of the passed `paths` have changed since the `base` commit.
pub fn has_changed_since(git_dir: &Path, base: &str, paths: &[&str]) -> bool {
let mut git = Command::new("git");
git.current_dir(git_dir);
run_git_diff_index(Some(git_dir), |cmd| {
cmd.args(["--quiet", base, "--"]).args(paths);

git.args(["diff-index", "--quiet", base, "--"]).args(paths);

// Exit code 0 => no changes
// Exit code 1 => some changes were detected
!git.status().expect("cannot run git diff-index").success()
// Exit code 0 => no changes
// Exit code 1 => some changes were detected
!cmd.status().expect("cannot run git diff-index").success()
})
}

/// Returns the latest upstream commit that modified `target_paths`, or `None` if no such commit
Expand Down Expand Up @@ -267,31 +266,49 @@ pub fn get_git_modified_files(
return Err("No upstream commit was found".to_string());
};

let mut git = Command::new("git");
if let Some(git_dir) = git_dir {
git.current_dir(git_dir);
}
let files = output_result(git.args(["diff-index", "--name-status", merge_base.trim()]))?
.lines()
.filter_map(|f| {
let (status, name) = f.trim().split_once(char::is_whitespace).unwrap();
if status == "D" {
None
} else if Path::new(name).extension().map_or(extensions.is_empty(), |ext| {
// If there is no extension, we allow the path if `extensions` is empty
// If there is an extension, we allow it if `extension` is empty or it contains the
// extension.
extensions.is_empty() || extensions.contains(&ext.to_str().unwrap())
}) {
Some(name.to_owned())
} else {
None
}
})
.collect();
let files = run_git_diff_index(git_dir, |cmd| {
output_result(cmd.args(["--name-status", merge_base.trim()]))
})?
.lines()
.filter_map(|f| {
let (status, name) = f.trim().split_once(char::is_whitespace).unwrap();
if status == "D" {
None
} else if Path::new(name).extension().map_or(extensions.is_empty(), |ext| {
// If there is no extension, we allow the path if `extensions` is empty
// If there is an extension, we allow it if `extension` is empty or it contains the
// extension.
extensions.is_empty() || extensions.contains(&ext.to_str().unwrap())
}) {
Some(name.to_owned())
} else {
None
}
})
.collect();
Ok(files)
}

/// diff-index can return outdated information, because it does not update the git index.
/// This function uses `update-index` to update the index first, and then provides `func` with a
/// command prepared to run `git diff-index`.
fn run_git_diff_index<F, T>(git_dir: Option<&Path>, func: F) -> T
where
F: FnOnce(&mut Command) -> T,
{
let git = || {
let mut git = Command::new("git");
if let Some(git_dir) = git_dir {
git.current_dir(git_dir);
}
git
};

// We ignore the exit code, as it errors out when some files are modified.
let _ = output_result(git().args(["update-index", "--refresh", "-q"]));
func(git().arg("diff-index"))
}

/// Returns the files that haven't been added to git yet.
pub fn get_git_untracked_files(git_dir: Option<&Path>) -> Result<Option<Vec<String>>, String> {
let mut git = Command::new("git");
Expand Down
45 changes: 27 additions & 18 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,30 @@ pub(crate) fn item_relative_path(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<Symbol>
tcx.def_path(def_id).data.into_iter().filter_map(|elem| elem.data.get_opt_name()).collect()
}

/// Get the public Rust path to an item. This is used to generate the URL to the item's page.
///
/// In particular: we handle macro differently: if it's not a macro 2.0 oe a built-in macro, then
/// it is generated at the top-level of the crate and its path will be `[crate_name, macro_name]`.
pub(crate) fn get_item_path(tcx: TyCtxt<'_>, def_id: DefId, kind: ItemType) -> Vec<Symbol> {
let crate_name = tcx.crate_name(def_id.krate);
let relative = item_relative_path(tcx, def_id);

if let ItemType::Macro = kind {
// Check to see if it is a macro 2.0 or built-in macro
// More information in <https://rust-lang.github.io/rfcs/1584-macros.html>.
if matches!(
CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx),
LoadedMacro::MacroDef { def, .. } if !def.macro_rules
) {
once(crate_name).chain(relative).collect()
} else {
vec![crate_name, *relative.last().expect("relative was empty")]
}
} else {
once(crate_name).chain(relative).collect()
}
}

/// Record an external fully qualified name in the external_paths cache.
///
/// These names are used later on by HTML rendering to generate things like
Expand All @@ -240,27 +264,12 @@ pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemT
return;
}

let crate_name = cx.tcx.crate_name(did.krate);

let relative = item_relative_path(cx.tcx, did);
let fqn = if let ItemType::Macro = kind {
// Check to see if it is a macro 2.0 or built-in macro
if matches!(
CStore::from_tcx(cx.tcx).load_macro_untracked(did, cx.tcx),
LoadedMacro::MacroDef { def, .. } if !def.macro_rules
) {
once(crate_name).chain(relative).collect()
} else {
vec![crate_name, *relative.last().expect("relative was empty")]
}
} else {
once(crate_name).chain(relative).collect()
};
let item_path = get_item_path(cx.tcx, did, kind);

if did.is_local() {
cx.cache.exact_paths.insert(did, fqn);
cx.cache.exact_paths.insert(did, item_path);
} else {
cx.cache.external_paths.insert(did, (fqn, kind));
cx.cache.external_paths.insert(did, (item_path, kind));
}
}

Expand Down
9 changes: 5 additions & 4 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::clean::utils::{is_literal_expr, print_evaluated_const};
use crate::core::DocContext;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::format::HrefInfo;
use crate::html::render::Context;
use crate::passes::collect_intra_doc_links::UrlFragment;

Expand Down Expand Up @@ -519,16 +520,16 @@ impl Item {
.iter()
.filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| {
debug!(?id);
if let Ok((mut href, ..)) = href(*id, cx) {
debug!(?href);
if let Ok(HrefInfo { mut url, .. }) = href(*id, cx) {
debug!(?url);
if let Some(ref fragment) = *fragment {
fragment.render(&mut href, cx.tcx())
fragment.render(&mut url, cx.tcx())
}
Some(RenderedLink {
original_text: s.clone(),
new_text: link_text.clone(),
tooltip: link_tooltip(*id, fragment, cx).to_string(),
href,
href: url,
})
} else {
None
Expand Down
11 changes: 5 additions & 6 deletions src/librustdoc/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ pub(crate) trait Joined: IntoIterator {
///
/// The performance of `joined` is slightly better than `format`, since it doesn't need to use a `Cell` to keep track of whether [`fmt`](Display::fmt)
/// was already called (`joined`'s API doesn't allow it be called more than once).
fn joined(self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result;
fn joined(&mut self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result;
}

impl<I, T> Joined for I
where
I: IntoIterator<Item = T>,
I: Iterator<Item = T>,
T: Display,
{
fn joined(self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result {
let mut iter = self.into_iter();
let Some(first) = iter.next() else { return Ok(()) };
fn joined(&mut self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result {
let Some(first) = self.next() else { return Ok(()) };
first.fmt(f)?;
for item in iter {
for item in self {
sep.fmt(f)?;
item.fmt(f)?;
}
Expand Down
Loading
Loading