Merge from rustc
This commit is contained in:
commit
9d579f5358
1487 changed files with 21017 additions and 14840 deletions
|
|
@ -1,6 +1,8 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use crate::ci::CiEnv;
|
||||
|
||||
pub struct GitConfig<'a> {
|
||||
pub git_repository: &'a str,
|
||||
pub nightly_branch: &'a str,
|
||||
|
|
@ -114,8 +116,8 @@ fn git_upstream_merge_base(
|
|||
|
||||
/// Searches for the nearest merge commit in the repository that also exists upstream.
|
||||
///
|
||||
/// If it fails to find the upstream remote, it then looks for the most recent commit made
|
||||
/// by the merge bot by matching the author's email address with the merge bot's email.
|
||||
/// It looks for the most recent commit made by the merge bot by matching the author's email
|
||||
/// address with the merge bot's email.
|
||||
pub fn get_closest_merge_commit(
|
||||
git_dir: Option<&Path>,
|
||||
config: &GitConfig<'_>,
|
||||
|
|
@ -127,7 +129,15 @@ pub fn get_closest_merge_commit(
|
|||
git.current_dir(git_dir);
|
||||
}
|
||||
|
||||
let merge_base = git_upstream_merge_base(config, git_dir).unwrap_or_else(|_| "HEAD".into());
|
||||
let merge_base = {
|
||||
if CiEnv::is_ci() {
|
||||
git_upstream_merge_base(config, git_dir).unwrap()
|
||||
} else {
|
||||
// For non-CI environments, ignore rust-lang/rust upstream as it usually gets
|
||||
// outdated very quickly.
|
||||
"HEAD".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
git.args([
|
||||
"rev-list",
|
||||
|
|
@ -196,67 +206,3 @@ pub fn get_git_untracked_files(
|
|||
.collect();
|
||||
Ok(Some(files))
|
||||
}
|
||||
|
||||
/// Print a warning if the branch returned from `updated_master_branch` is old
|
||||
///
|
||||
/// For certain configurations of git repository, this remote will not be
|
||||
/// updated when running `git pull`.
|
||||
///
|
||||
/// This can result in formatting thousands of files instead of a dozen,
|
||||
/// so we should warn the user something is wrong.
|
||||
pub fn warn_old_master_branch(config: &GitConfig<'_>, git_dir: &Path) {
|
||||
if crate::ci::CiEnv::is_ci() {
|
||||
// this warning is useless in CI,
|
||||
// and CI probably won't have the right branches anyway.
|
||||
return;
|
||||
}
|
||||
// this will be overwritten by the actual name, if possible
|
||||
let mut updated_master = "the upstream master branch".to_string();
|
||||
match warn_old_master_branch_(config, git_dir, &mut updated_master) {
|
||||
Ok(branch_is_old) => {
|
||||
if !branch_is_old {
|
||||
return;
|
||||
}
|
||||
// otherwise fall through and print the rest of the warning
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("warning: unable to check if {updated_master} is old due to error: {err}")
|
||||
}
|
||||
}
|
||||
eprintln!(
|
||||
"warning: {updated_master} is used to determine if files have been modified\n\
|
||||
warning: if it is not updated, this may cause files to be needlessly reformatted"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn warn_old_master_branch_(
|
||||
config: &GitConfig<'_>,
|
||||
git_dir: &Path,
|
||||
updated_master: &mut String,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
use std::time::Duration;
|
||||
*updated_master = updated_master_branch(config, Some(git_dir))?;
|
||||
let branch_path = git_dir.join(".git/refs/remotes").join(&updated_master);
|
||||
const WARN_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 10);
|
||||
let meta = match std::fs::metadata(&branch_path) {
|
||||
Ok(meta) => meta,
|
||||
Err(err) => {
|
||||
let gcd = git_common_dir(&git_dir)?;
|
||||
if branch_path.starts_with(&gcd) {
|
||||
return Err(Box::new(err));
|
||||
}
|
||||
std::fs::metadata(Path::new(&gcd).join("refs/remotes").join(&updated_master))?
|
||||
}
|
||||
};
|
||||
if meta.modified()?.elapsed()? > WARN_AFTER {
|
||||
eprintln!("warning: {updated_master} has not been updated in 10 days");
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn git_common_dir(dir: &Path) -> Result<String, String> {
|
||||
output_result(Command::new("git").arg("-C").arg(dir).arg("rev-parse").arg("--git-common-dir"))
|
||||
.map(|x| x.trim().to_string())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 80d82ca22abbee5fb7b51fa1abeb1ae34e99e88a
|
||||
Subproject commit 15fbd2f607d4defc87053b8b76bf5038f2483cf4
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_ast::{MetaItemKind, NestedMetaItem};
|
||||
use rustc_ast::{MetaItemInner, MetaItemKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
|
||||
pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) {
|
||||
// Check if the reason is present
|
||||
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
|
||||
if let Some(item) = items.last().and_then(MetaItemInner::meta_item)
|
||||
&& let MetaItemKind::NameValue(_) = &item.kind
|
||||
&& item.path == sym::reason
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
|
||||
use super::utils::extract_clippy_lint;
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_lint::{LateContext, Level, LintContext};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{DUMMY_SP, sym};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) {
|
||||
for lint in items {
|
||||
if let Some(lint_name) = extract_clippy_lint(lint) {
|
||||
if lint_name.as_str() == "restriction" && name != sym::allow {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ mod utils;
|
|||
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
|
||||
use rustc_ast::{Attribute, MetaItemInner, MetaItemKind};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -456,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
return;
|
||||
}
|
||||
for item in items {
|
||||
if let NestedMetaItem::MetaItem(mi) = &item
|
||||
if let MetaItemInner::MetaItem(mi) = &item
|
||||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use super::{Attribute, NON_MINIMAL_CFG};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use rustc_ast::{MetaItemKind, NestedMetaItem};
|
||||
use rustc_ast::{MetaItemInner, MetaItemKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::EarlyContext;
|
||||
use rustc_span::sym;
|
||||
|
|
@ -14,9 +14,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) {
|
||||
for item in items {
|
||||
if let NestedMetaItem::MetaItem(meta) = item {
|
||||
if let MetaItemInner::MetaItem(meta) = item {
|
||||
if !meta.has_name(sym::any) && !meta.has_name(sym::all) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word};
|
|||
use super::{Attribute, USELESS_ATTRIBUTE};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{SpanRangeExt, first_line_of_span};
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
|
|
@ -21,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
|
|||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
if let NestedMetaItem::MetaItem(meta_item) = lint
|
||||
if let MetaItemInner::MetaItem(meta_item) = lint
|
||||
&& meta_item.is_word()
|
||||
&& let Some(ident) = meta_item.ident()
|
||||
&& matches!(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::macros::{is_panic, macro_backtrace};
|
||||
use rustc_ast::{AttrId, NestedMetaItem};
|
||||
use rustc_ast::{AttrId, MetaItemInner};
|
||||
use rustc_hir::{
|
||||
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
|
||||
};
|
||||
|
|
@ -8,8 +8,8 @@ use rustc_middle::ty;
|
|||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
pub(super) fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
|
||||
if let NestedMetaItem::MetaItem(mi) = &nmi {
|
||||
pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
|
||||
if let MetaItemInner::MetaItem(mi) = &nmi {
|
||||
mi.is_word() && mi.has_name(expected)
|
||||
} else {
|
||||
false
|
||||
|
|
@ -74,7 +74,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>
|
|||
}
|
||||
|
||||
/// Returns the lint name if it is clippy lint.
|
||||
pub(super) fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
|
||||
pub(super) fn extract_clippy_lint(lint: &MetaItemInner) -> Option<Symbol> {
|
||||
if let Some(meta_item) = lint.meta_item()
|
||||
&& meta_item.path.segments.len() > 1
|
||||
&& let tool_name = meta_item.path.segments[0].ident
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ impl EarlyLintPass for CfgNotTest {
|
|||
}
|
||||
}
|
||||
|
||||
fn contains_not_test(list: Option<&[NestedMetaItem]>, not: bool) -> bool {
|
||||
fn contains_not_test(list: Option<&[MetaItemInner]>, not: bool) -> bool {
|
||||
list.is_some_and(|list| {
|
||||
list.iter().any(|item| {
|
||||
item.ident().is_some_and(|ident| match ident.name {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
|
||||
use rustc_hir::{
|
||||
BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
|
||||
PredicateOrigin, Ty, TyKind, WherePredicate,
|
||||
PredicateOrigin, Ty, WherePredicate,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -199,12 +199,6 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
|
|||
fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
|
||||
if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
|
||||
self.ty_params.remove(&def_id);
|
||||
} else if let TyKind::OpaqueDef(id, _) = t.kind {
|
||||
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
|
||||
// `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
|
||||
// using `OnlyBodies`, so the check ends up failing and the type isn't fully walked.
|
||||
let item = self.nested_visit_map().item(id);
|
||||
walk_item(self, item);
|
||||
} else {
|
||||
walk_ty(self, t);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
|
|||
use rustc_errors::{Applicability, SuggestionStyle};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{
|
||||
AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier,
|
||||
AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier,
|
||||
TyKind, WherePredicate,
|
||||
};
|
||||
use rustc_hir_analysis::lower_ty;
|
||||
|
|
@ -342,11 +342,8 @@ impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &rustc_hir::Ty<'_>) {
|
||||
if let TyKind::OpaqueDef(item_id, ..) = ty.kind
|
||||
&& let item = cx.tcx.hir().item(item_id)
|
||||
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
|
||||
{
|
||||
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx>) {
|
||||
if let TyKind::OpaqueDef(opaque_ty, ..) = ty.kind {
|
||||
check(cx, opaque_ty.bounds);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::higher::IfLet;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirId;
|
||||
|
|
@ -133,7 +133,7 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
|
|||
.index_use
|
||||
.iter()
|
||||
.map(|(index, _)| *index)
|
||||
.collect::<FxHashSet<_>>();
|
||||
.collect::<FxIndexSet<_>>();
|
||||
|
||||
let value_name = |index| format!("{}_{index}", slice.ident.name);
|
||||
|
||||
|
|
|
|||
|
|
@ -308,11 +308,7 @@ enum LenOutput {
|
|||
|
||||
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
||||
if let ty::Alias(_, alias_ty) = ty.kind()
|
||||
&& let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
|
||||
&& let Item {
|
||||
kind: ItemKind::OpaqueTy(opaque),
|
||||
..
|
||||
} = item
|
||||
&& let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
|
||||
&& let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin
|
||||
&& let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds
|
||||
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#![feature(array_windows)]
|
||||
#![feature(binary_heap_into_iter_sorted)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
@ -27,8 +26,6 @@
|
|||
unused_qualifications,
|
||||
rustc::internal
|
||||
)]
|
||||
// Disable this rustc lint for now, as it was also done in rustc
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
||||
// FIXME: switch to something more ergonomic here, once available.
|
||||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::FnRetTy::Return;
|
||||
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
||||
use rustc_hir::intravisit::{
|
||||
Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
|
||||
Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound,
|
||||
walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate,
|
||||
};
|
||||
use rustc_hir::{
|
||||
|
|
@ -311,7 +311,7 @@ fn could_use_elision<'tcx>(
|
|||
Some((elidable_lts, usages))
|
||||
}
|
||||
|
||||
fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> {
|
||||
fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefId> {
|
||||
named_generics
|
||||
.iter()
|
||||
.filter_map(|par| {
|
||||
|
|
@ -420,11 +420,9 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> {
|
|||
|
||||
fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
|
||||
match ty.kind {
|
||||
TyKind::OpaqueDef(item, bounds) => {
|
||||
let map = self.cx.tcx.hir();
|
||||
let item = map.item(item);
|
||||
TyKind::OpaqueDef(opaque, bounds) => {
|
||||
let len = self.lts.len();
|
||||
walk_item(self, item);
|
||||
self.visit_opaque_ty(opaque);
|
||||
self.lts.truncate(len);
|
||||
self.lts.extend(bounds.iter().filter_map(|bound| match bound {
|
||||
GenericArg::Lifetime(&l) => Some(l),
|
||||
|
|
@ -499,7 +497,7 @@ struct Usage {
|
|||
|
||||
struct LifetimeChecker<'cx, 'tcx, F> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
map: FxHashMap<LocalDefId, Vec<Usage>>,
|
||||
map: FxIndexMap<LocalDefId, Vec<Usage>>,
|
||||
where_predicate_depth: usize,
|
||||
generic_args_depth: usize,
|
||||
phantom: std::marker::PhantomData<F>,
|
||||
|
|
@ -621,7 +619,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
|
|||
fn report_elidable_impl_lifetimes<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
impl_: &'tcx Impl<'_>,
|
||||
map: &FxHashMap<LocalDefId, Vec<Usage>>,
|
||||
map: &FxIndexMap<LocalDefId, Vec<Usage>>,
|
||||
) {
|
||||
let single_usages = map
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::ty::has_iter_method;
|
|||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg};
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
|
|
@ -39,7 +39,7 @@ pub(super) fn check<'tcx>(
|
|||
var: canonical_id,
|
||||
indexed_mut: FxHashSet::default(),
|
||||
indexed_indirectly: FxHashMap::default(),
|
||||
indexed_directly: FxHashMap::default(),
|
||||
indexed_directly: FxIndexMap::default(),
|
||||
referenced: FxHashSet::default(),
|
||||
nonindex: false,
|
||||
prefer_mutable: false,
|
||||
|
|
@ -229,7 +229,7 @@ struct VarVisitor<'a, 'tcx> {
|
|||
indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
|
||||
/// subset of `indexed` of vars that are indexed directly: `v[i]`
|
||||
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
|
||||
indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
|
||||
indexed_directly: FxIndexMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
|
||||
/// Any names that are used outside an index operation.
|
||||
/// Used to detect things like `&mut vec` used together with `vec[i]`
|
||||
referenced: FxHashSet<Symbol>,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl,
|
||||
FnRetTy, GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, TraitRef, Ty, TyKind,
|
||||
FnRetTy, GenericArg, GenericBound, ImplItem, Item, LifetimeName, Node, TraitRef, Ty, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -105,9 +105,7 @@ fn future_trait_ref<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
ty: &'tcx Ty<'tcx>,
|
||||
) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
|
||||
if let TyKind::OpaqueDef(item_id, bounds) = ty.kind
|
||||
&& let item = cx.tcx.hir().item(item_id)
|
||||
&& let ItemKind::OpaqueTy(opaque) = &item.kind
|
||||
if let TyKind::OpaqueDef(opaque, bounds) = ty.kind
|
||||
&& let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
|
||||
if let GenericBound::Trait(poly, _) = bound {
|
||||
Some(&poly.trait_ref)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
|
|||
use clippy_utils::{get_attr, is_lint_allowed};
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
|
||||
|
|
@ -475,19 +476,19 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> {
|
|||
|
||||
struct ArmSigDropHelper<'a, 'tcx> {
|
||||
sig_drop_checker: SigDropChecker<'a, 'tcx>,
|
||||
found_sig_drop_spans: FxHashSet<Span>,
|
||||
found_sig_drop_spans: FxIndexSet<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
|
||||
ArmSigDropHelper {
|
||||
sig_drop_checker: SigDropChecker::new(cx),
|
||||
found_sig_drop_spans: FxHashSet::<Span>::default(),
|
||||
found_sig_drop_spans: FxIndexSet::<Span>::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet<Span> {
|
||||
fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxIndexSet<Span> {
|
||||
let mut helper = ArmSigDropHelper::new(cx);
|
||||
for arm in arms {
|
||||
helper.visit_expr(arm);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use clippy_utils::visitors::for_each_expr_without_closures;
|
|||
use clippy_utils::{eq_expr_value, hash_expr, higher};
|
||||
use rustc_ast::{LitKind, RangeLimits};
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_data_structures::unhash::UnhashMap;
|
||||
use rustc_data_structures::unhash::UnindexMap;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -226,7 +226,7 @@ fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
|
|||
}
|
||||
|
||||
/// Checks if the expression is an index into a slice and adds it to `indexes`
|
||||
fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
|
||||
fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
|
||||
if let ExprKind::Index(slice, index_lit, _) = expr.kind
|
||||
&& cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice()
|
||||
&& let Some(index) = upper_index_expr(index_lit)
|
||||
|
|
@ -274,7 +274,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh
|
|||
}
|
||||
|
||||
/// Checks if the expression is an `assert!` expression and adds it to `asserts`
|
||||
fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
|
||||
fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
|
||||
if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
|
||||
let hash = hash_expr(cx, slice);
|
||||
let indexes = map.entry(hash).or_default();
|
||||
|
|
@ -311,7 +311,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
|
|||
/// Inspects indexes and reports lints.
|
||||
///
|
||||
/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
|
||||
fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>>) {
|
||||
fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>>>) {
|
||||
for bucket in map.values() {
|
||||
for entry in bucket {
|
||||
let Some(full_span) = entry
|
||||
|
|
@ -403,7 +403,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
|
|||
|
||||
impl LateLintPass<'_> for MissingAssertsForIndexing {
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
|
||||
let mut map = UnhashMap::default();
|
||||
let mut map = UnindexMap::default();
|
||||
|
||||
for_each_expr_without_closures(body.value, |expr| {
|
||||
check_index(cx, expr, &mut map);
|
||||
|
|
|
|||
|
|
@ -193,8 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
|||
| hir::ItemKind::Trait(..)
|
||||
| hir::ItemKind::TraitAlias(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::OpaqueTy(..) => {},
|
||||
| hir::ItemKind::Union(..) => {}
|
||||
hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
|
|
|
|||
|
|
@ -130,7 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
| hir::ItemKind::GlobalAsm(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::OpaqueTy(..)
|
||||
| hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::Impl { .. }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
|
|
@ -87,7 +87,7 @@ impl EarlyLintPass for ModStyle {
|
|||
|
||||
// `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
|
||||
// `[path, to]` but not foo
|
||||
let mut folder_segments = FxHashSet::default();
|
||||
let mut folder_segments = FxIndexSet::default();
|
||||
// `mod_folders` is all the unique folder names that contain a mod.rs file
|
||||
let mut mod_folders = FxHashSet::default();
|
||||
// `file_map` maps file names to the full path including the file name
|
||||
|
|
@ -144,7 +144,7 @@ impl EarlyLintPass for ModStyle {
|
|||
/// is `mod.rs` we add it's parent folder to `mod_folders`.
|
||||
fn process_paths_for_mod_files<'a>(
|
||||
path: &'a Path,
|
||||
folder_segments: &mut FxHashSet<&'a OsStr>,
|
||||
folder_segments: &mut FxIndexSet<&'a OsStr>,
|
||||
mod_folders: &mut FxHashSet<&'a OsStr>,
|
||||
) {
|
||||
let mut comp = path.components().rev().peekable();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::source::snippet;
|
|||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
|
|
@ -101,7 +101,7 @@ fn check_closures<'tcx>(
|
|||
ctx: &mut MutablyUsedVariablesCtxt<'tcx>,
|
||||
cx: &LateContext<'tcx>,
|
||||
checked_closures: &mut FxHashSet<LocalDefId>,
|
||||
closures: FxHashSet<LocalDefId>,
|
||||
closures: FxIndexSet<LocalDefId>,
|
||||
) {
|
||||
let hir = cx.tcx.hir();
|
||||
for closure in closures {
|
||||
|
|
@ -196,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
|||
prev_bind: None,
|
||||
prev_move_to_closure: HirIdSet::default(),
|
||||
aliases: HirIdMap::default(),
|
||||
async_closures: FxHashSet::default(),
|
||||
async_closures: FxIndexSet::default(),
|
||||
tcx: cx.tcx,
|
||||
};
|
||||
euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx)
|
||||
|
|
@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
|||
|
||||
// We retrieve all the closures declared in the function because they will not be found
|
||||
// by `euv::Delegate`.
|
||||
let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
|
||||
let mut closures: FxIndexSet<LocalDefId> = FxIndexSet::default();
|
||||
for_each_expr(cx, body, |expr| {
|
||||
if let ExprKind::Closure(closure) = expr.kind {
|
||||
closures.insert(closure.def_id);
|
||||
|
|
@ -307,7 +307,7 @@ struct MutablyUsedVariablesCtxt<'tcx> {
|
|||
/// use of a variable.
|
||||
prev_move_to_closure: HirIdSet,
|
||||
aliases: HirIdMap<HirId>,
|
||||
async_closures: FxHashSet<LocalDefId>,
|
||||
async_closures: FxIndexSet<LocalDefId>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use clippy_utils::{
|
|||
path_to_local_id, span_contains_cfg, span_find_starting_semi,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
|
@ -407,7 +407,7 @@ fn check_final_expr<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if ret_span.from_expansion() {
|
||||
if ret_span.from_expansion() || is_from_proc_macro(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -421,7 +421,7 @@ fn check_final_expr<'tcx>(
|
|||
if matches!(Level::from_attr(attr), Some(Level::Expect(_)))
|
||||
&& let metas = attr.meta_item_list()
|
||||
&& let Some(lst) = metas
|
||||
&& let [NestedMetaItem::MetaItem(meta_item), ..] = lst.as_slice()
|
||||
&& let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()
|
||||
&& let [tool, lint_name] = meta_item.path.segments.as_slice()
|
||||
&& tool.ident.name == sym::clippy
|
||||
&& matches!(
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
|||
use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core};
|
||||
use itertools::Itertools;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
|
||||
use crate::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -334,7 +334,7 @@ struct IndexBinding<'a, 'tcx> {
|
|||
|
||||
impl<'tcx> IndexBinding<'_, 'tcx> {
|
||||
fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String {
|
||||
let mut bindings = FxHashSet::default();
|
||||
let mut bindings = FxIndexSet::default();
|
||||
for expr in exprs {
|
||||
bindings.insert(self.snippet_index_binding(expr));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
|
|||
use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, IndexEntry};
|
||||
use rustc_data_structures::unhash::UnhashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
|
@ -16,7 +16,6 @@ use rustc_hir::{
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -427,7 +426,7 @@ fn rollup_traits(
|
|||
bounds: &[GenericBound<'_>],
|
||||
msg: &'static str,
|
||||
) -> Vec<(ComparableTraitRef, Span)> {
|
||||
let mut map = FxHashMap::default();
|
||||
let mut map = FxIndexMap::default();
|
||||
let mut repeated_res = false;
|
||||
|
||||
let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
|
||||
|
|
@ -442,8 +441,8 @@ fn rollup_traits(
|
|||
for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
|
||||
let (comparable_bound, span_direct) = bound;
|
||||
match map.entry(comparable_bound) {
|
||||
Entry::Occupied(_) => repeated_res = true,
|
||||
Entry::Vacant(e) => {
|
||||
IndexEntry::Occupied(_) => repeated_res = true,
|
||||
IndexEntry::Vacant(e) => {
|
||||
e.insert((span_direct, i));
|
||||
i += 1;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -85,10 +85,6 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
|
||||
if matches!(item.kind, ItemKind::OpaqueTy(_)) {
|
||||
// skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
|
||||
return;
|
||||
}
|
||||
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
|
||||
// relevant for linting, since this is the self type of the `impl` we're currently in. To
|
||||
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
|
||||
|
|
@ -130,10 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
self.stack.push(stack_item);
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
|
||||
if !matches!(item.kind, ItemKind::OpaqueTy(_)) {
|
||||
self.stack.pop();
|
||||
}
|
||||
fn check_item_post(&mut self, _: &LateContext<'_>, _: &Item<'_>) {
|
||||
self.stack.pop();
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
|
||||
|
|
|
|||
|
|
@ -141,62 +141,89 @@ fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) {
|
|||
|
||||
/// Get the search patterns to use for the given expression
|
||||
fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
|
||||
match e.kind {
|
||||
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
|
||||
// Parenthesis are trimmed from the text before the search patterns are matched.
|
||||
// See: `span_matches_pat`
|
||||
ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
|
||||
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Lit(lit) => lit_search_pat(&lit.node),
|
||||
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
|
||||
ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
|
||||
ExprKind::Call(first, [.., last])
|
||||
| ExprKind::MethodCall(_, first, [.., last], _)
|
||||
| ExprKind::Binary(_, first, last)
|
||||
| ExprKind::Tup([first, .., last])
|
||||
| ExprKind::Assign(first, last, _)
|
||||
| ExprKind::AssignOp(_, first, last) => (expr_search_pat(tcx, first).0, expr_search_pat(tcx, last).1),
|
||||
ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat(tcx, e),
|
||||
ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("")),
|
||||
ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat(tcx, let_expr.init).1),
|
||||
ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
|
||||
(Pat::Str("for"), Pat::Str("}"))
|
||||
},
|
||||
ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
|
||||
ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => (expr_search_pat(tcx, e).0, Pat::Str("?")),
|
||||
ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
|
||||
(expr_search_pat(tcx, e).0, Pat::Str("await"))
|
||||
},
|
||||
ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, tcx.hir().body(body).value).1),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
..
|
||||
fn expr_search_pat_inner(tcx: TyCtxt<'_>, e: &Expr<'_>, outer_span: Span) -> (Pat, Pat) {
|
||||
// The expression can have subexpressions in different contexts, in which case
|
||||
// building up a search pattern from the macro expansion would lead to false positives;
|
||||
// e.g. `return format!(..)` would be considered to be from a proc macro
|
||||
// if we build up a pattern for the macro expansion and compare it to the invocation `format!()`.
|
||||
// So instead we return an empty pattern such that `span_matches_pat` always returns true.
|
||||
if !e.span.eq_ctxt(outer_span) {
|
||||
return (Pat::Str(""), Pat::Str(""));
|
||||
}
|
||||
|
||||
match e.kind {
|
||||
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
|
||||
// Parenthesis are trimmed from the text before the search patterns are matched.
|
||||
// See: `span_matches_pat`
|
||||
ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
|
||||
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
ExprKind::Lit(lit) => lit_search_pat(&lit.node),
|
||||
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
|
||||
ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => {
|
||||
(expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("("))
|
||||
},
|
||||
None,
|
||||
) => (Pat::Str("unsafe"), Pat::Str("}")),
|
||||
ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
|
||||
ExprKind::Field(e, name) => (expr_search_pat(tcx, e).0, Pat::Sym(name.name)),
|
||||
ExprKind::Index(e, _, _) => (expr_search_pat(tcx, e).0, Pat::Str("]")),
|
||||
ExprKind::Path(ref path) => qpath_search_pat(path),
|
||||
ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
|
||||
ExprKind::Break(Destination { label: Some(name), .. }, None) => (Pat::Str("break"), Pat::Sym(name.ident.name)),
|
||||
ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
|
||||
ExprKind::Continue(Destination { label: Some(name), .. }) => (Pat::Str("continue"), Pat::Sym(name.ident.name)),
|
||||
ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
|
||||
ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
|
||||
ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat(tcx, e).1),
|
||||
_ => (Pat::Str(""), Pat::Str("")),
|
||||
ExprKind::Call(first, [.., last])
|
||||
| ExprKind::MethodCall(_, first, [.., last], _)
|
||||
| ExprKind::Binary(_, first, last)
|
||||
| ExprKind::Tup([first, .., last])
|
||||
| ExprKind::Assign(first, last, _)
|
||||
| ExprKind::AssignOp(_, first, last) => (
|
||||
expr_search_pat_inner(tcx, first, outer_span).0,
|
||||
expr_search_pat_inner(tcx, last, outer_span).1,
|
||||
),
|
||||
ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat_inner(tcx, e, outer_span),
|
||||
ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("")),
|
||||
ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat_inner(tcx, let_expr.init, outer_span).1),
|
||||
ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
|
||||
(Pat::Str("for"), Pat::Str("}"))
|
||||
},
|
||||
ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
|
||||
ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => {
|
||||
(expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("?"))
|
||||
},
|
||||
ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
|
||||
(expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("await"))
|
||||
},
|
||||
ExprKind::Closure(&Closure { body, .. }) => (
|
||||
Pat::Str(""),
|
||||
expr_search_pat_inner(tcx, tcx.hir().body(body).value, outer_span).1,
|
||||
),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
..
|
||||
},
|
||||
None,
|
||||
) => (Pat::Str("unsafe"), Pat::Str("}")),
|
||||
ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
|
||||
ExprKind::Field(e, name) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Sym(name.name)),
|
||||
ExprKind::Index(e, _, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("]")),
|
||||
ExprKind::Path(ref path) => qpath_search_pat(path),
|
||||
ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
|
||||
ExprKind::Break(Destination { label: Some(name), .. }, None) => {
|
||||
(Pat::Str("break"), Pat::Sym(name.ident.name))
|
||||
},
|
||||
ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
|
||||
ExprKind::Continue(Destination { label: Some(name), .. }) => {
|
||||
(Pat::Str("continue"), Pat::Sym(name.ident.name))
|
||||
},
|
||||
ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
|
||||
ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
|
||||
ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat_inner(tcx, e, outer_span).1),
|
||||
_ => (Pat::Str(""), Pat::Str("")),
|
||||
}
|
||||
}
|
||||
|
||||
expr_search_pat_inner(tcx, e, e.span)
|
||||
}
|
||||
|
||||
fn fn_header_search_pat(header: FnHeader) -> Pat {
|
||||
|
|
@ -220,7 +247,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
|
|||
ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
|
||||
ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
|
||||
ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
|
||||
ItemKind::TyAlias(..) | ItemKind::OpaqueTy(_) => (Pat::Str("type"), Pat::Str(";")),
|
||||
ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
|
||||
ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
|
||||
ItemKind::Struct(VariantData::Struct { .. }, _) => (Pat::Str("struct"), Pat::Str("}")),
|
||||
ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#![feature(array_chunks)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ impl SourceFileRange {
|
|||
self.sf
|
||||
.src
|
||||
.as_ref()
|
||||
.map(|src| src.as_str())
|
||||
.or_else(|| self.sf.external_src.get().and_then(|src| src.get_source()))
|
||||
.and_then(|x| x.get(self.range.clone()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ mod issue9612 {
|
|||
util();
|
||||
}
|
||||
|
||||
#[allow(unconditional_panic)]
|
||||
fn util() {
|
||||
let _a: u8 = 4.try_into().unwrap();
|
||||
let _a: u8 = 5.try_into().expect("");
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ mod issue9612 {
|
|||
util();
|
||||
}
|
||||
|
||||
#[allow(unconditional_panic)]
|
||||
fn util() {
|
||||
let _a: u8 = 4.try_into().unwrap();
|
||||
let _a: u8 = 5.try_into().expect("");
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ LL | let _ = &boxed_slice[1];
|
|||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `.get().unwrap()` on a slice
|
||||
--> tests/ui-toml/unwrap_used/unwrap_used.rs:93:17
|
||||
--> tests/ui-toml/unwrap_used/unwrap_used.rs:94:17
|
||||
|
|
||||
LL | let _ = Box::new([0]).get(1).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
#![allow(incomplete_features)]
|
||||
#![feature(unnamed_fields)]
|
||||
|
||||
#[repr(C)]
|
||||
struct Foo {
|
||||
_: struct {
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)]
|
||||
|
||||
fn foo(n: u32) -> u32 {
|
||||
if let Some(n) = n.checked_sub(4) { n } else { n }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)]
|
||||
|
||||
fn foo(n: u32) -> u32 {
|
||||
if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#![allow(dead_code, incomplete_features)]
|
||||
#![warn(clippy::doc_markdown)]
|
||||
#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
|
||||
#![feature(custom_inner_attributes, generic_const_exprs)]
|
||||
#![rustfmt::skip]
|
||||
|
||||
/// The `foo_bar` function does _nothing_. See also `foo::bar`. (note the dot there)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#![allow(dead_code, incomplete_features)]
|
||||
#![warn(clippy::doc_markdown)]
|
||||
#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
|
||||
#![feature(custom_inner_attributes, generic_const_exprs)]
|
||||
#![rustfmt::skip]
|
||||
|
||||
/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ fn main() {
|
|||
mod issue9909 {
|
||||
#![allow(clippy::identity_op, clippy::unwrap_used, dead_code)]
|
||||
|
||||
#[allow(unconditional_panic)]
|
||||
fn reduced() {
|
||||
let f = &[1, 2, 3];
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ fn main() {
|
|||
mod issue9909 {
|
||||
#![allow(clippy::identity_op, clippy::unwrap_used, dead_code)]
|
||||
|
||||
#[allow(unconditional_panic)]
|
||||
fn reduced() {
|
||||
let f = &[1, 2, 3];
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
|
|||
= help: consider using `expect()` to provide a better panic message
|
||||
|
||||
error: called `.get().unwrap()` on a slice
|
||||
--> tests/ui/get_unwrap.rs:77:24
|
||||
--> tests/ui/get_unwrap.rs:78:24
|
||||
|
|
||||
LL | let _x: &i32 = f.get(1 + 2).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -277,7 +277,7 @@ LL | let _x: &i32 = &f[1 + 2];
|
|||
| ~~~~~~~~~
|
||||
|
||||
error: called `.get().unwrap()` on a slice
|
||||
--> tests/ui/get_unwrap.rs:80:18
|
||||
--> tests/ui/get_unwrap.rs:81:18
|
||||
|
|
||||
LL | let _x = f.get(1 + 2).unwrap().to_string();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -288,7 +288,7 @@ LL | let _x = f[1 + 2].to_string();
|
|||
| ~~~~~~~~
|
||||
|
||||
error: called `.get().unwrap()` on a slice
|
||||
--> tests/ui/get_unwrap.rs:83:18
|
||||
--> tests/ui/get_unwrap.rs:84:18
|
||||
|
|
||||
LL | let _x = f.get(1 + 2).unwrap().abs();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -299,7 +299,7 @@ LL | let _x = f[1 + 2].abs();
|
|||
| ~~~~~~~~
|
||||
|
||||
error: called `.get_mut().unwrap()` on a slice
|
||||
--> tests/ui/get_unwrap.rs:100:33
|
||||
--> tests/ui/get_unwrap.rs:101:33
|
||||
|
|
||||
LL | let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//@aux-build:proc_macros.rs
|
||||
#![feature(yeet_expr)]
|
||||
#![allow(unused)]
|
||||
#![allow(
|
||||
|
|
@ -9,6 +10,9 @@
|
|||
)]
|
||||
#![warn(clippy::needless_return)]
|
||||
|
||||
extern crate proc_macros;
|
||||
use proc_macros::with_span;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
macro_rules! the_answer {
|
||||
|
|
@ -359,6 +363,10 @@ fn issue12907() -> String {
|
|||
"".split("").next().unwrap().to_string()
|
||||
}
|
||||
|
||||
fn issue13458() {
|
||||
with_span!(span return);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn a(x: Option<u8>) -> Option<u8> {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//@aux-build:proc_macros.rs
|
||||
#![feature(yeet_expr)]
|
||||
#![allow(unused)]
|
||||
#![allow(
|
||||
|
|
@ -9,6 +10,9 @@
|
|||
)]
|
||||
#![warn(clippy::needless_return)]
|
||||
|
||||
extern crate proc_macros;
|
||||
use proc_macros::with_span;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
macro_rules! the_answer {
|
||||
|
|
@ -369,6 +373,10 @@ fn issue12907() -> String {
|
|||
return "".split("").next().unwrap().to_string();
|
||||
}
|
||||
|
||||
fn issue13458() {
|
||||
with_span!(span return);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn a(x: Option<u8>) -> Option<u8> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:25:5
|
||||
--> tests/ui/needless_return.rs:29:5
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -13,7 +13,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:29:5
|
||||
--> tests/ui/needless_return.rs:33:5
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -25,7 +25,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:34:5
|
||||
--> tests/ui/needless_return.rs:38:5
|
||||
|
|
||||
LL | return true;;;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -37,7 +37,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:39:5
|
||||
--> tests/ui/needless_return.rs:43:5
|
||||
|
|
||||
LL | return true;; ; ;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -49,7 +49,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:44:9
|
||||
--> tests/ui/needless_return.rs:48:9
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -61,7 +61,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:46:9
|
||||
--> tests/ui/needless_return.rs:50:9
|
||||
|
|
||||
LL | return false;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -73,7 +73,7 @@ LL + false
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:52:17
|
||||
--> tests/ui/needless_return.rs:56:17
|
||||
|
|
||||
LL | true => return false,
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -84,7 +84,7 @@ LL | true => false,
|
|||
| ~~~~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:54:13
|
||||
--> tests/ui/needless_return.rs:58:13
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -96,7 +96,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:61:9
|
||||
--> tests/ui/needless_return.rs:65:9
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -108,7 +108,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:63:16
|
||||
--> tests/ui/needless_return.rs:67:16
|
||||
|
|
||||
LL | let _ = || return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -119,7 +119,7 @@ LL | let _ = || true;
|
|||
| ~~~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:67:5
|
||||
--> tests/ui/needless_return.rs:71:5
|
||||
|
|
||||
LL | return the_answer!();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -131,7 +131,7 @@ LL + the_answer!()
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:70:21
|
||||
--> tests/ui/needless_return.rs:74:21
|
||||
|
|
||||
LL | fn test_void_fun() {
|
||||
| _____________________^
|
||||
|
|
@ -146,7 +146,7 @@ LL + fn test_void_fun() {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:75:11
|
||||
--> tests/ui/needless_return.rs:79:11
|
||||
|
|
||||
LL | if b {
|
||||
| ___________^
|
||||
|
|
@ -161,7 +161,7 @@ LL + if b {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:77:13
|
||||
--> tests/ui/needless_return.rs:81:13
|
||||
|
|
||||
LL | } else {
|
||||
| _____________^
|
||||
|
|
@ -176,7 +176,7 @@ LL + } else {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:85:14
|
||||
--> tests/ui/needless_return.rs:89:14
|
||||
|
|
||||
LL | _ => return,
|
||||
| ^^^^^^
|
||||
|
|
@ -187,7 +187,7 @@ LL | _ => (),
|
|||
| ~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:93:24
|
||||
--> tests/ui/needless_return.rs:97:24
|
||||
|
|
||||
LL | let _ = 42;
|
||||
| ________________________^
|
||||
|
|
@ -202,7 +202,7 @@ LL + let _ = 42;
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:96:14
|
||||
--> tests/ui/needless_return.rs:100:14
|
||||
|
|
||||
LL | _ => return,
|
||||
| ^^^^^^
|
||||
|
|
@ -213,7 +213,7 @@ LL | _ => (),
|
|||
| ~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:109:9
|
||||
--> tests/ui/needless_return.rs:113:9
|
||||
|
|
||||
LL | return String::from("test");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -225,7 +225,7 @@ LL + String::from("test")
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:111:9
|
||||
--> tests/ui/needless_return.rs:115:9
|
||||
|
|
||||
LL | return String::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -237,7 +237,7 @@ LL + String::new()
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:133:32
|
||||
--> tests/ui/needless_return.rs:137:32
|
||||
|
|
||||
LL | bar.unwrap_or_else(|_| return)
|
||||
| ^^^^^^
|
||||
|
|
@ -248,7 +248,7 @@ LL | bar.unwrap_or_else(|_| {})
|
|||
| ~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:137:21
|
||||
--> tests/ui/needless_return.rs:141:21
|
||||
|
|
||||
LL | let _ = || {
|
||||
| _____________________^
|
||||
|
|
@ -263,7 +263,7 @@ LL + let _ = || {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:140:20
|
||||
--> tests/ui/needless_return.rs:144:20
|
||||
|
|
||||
LL | let _ = || return;
|
||||
| ^^^^^^
|
||||
|
|
@ -274,7 +274,7 @@ LL | let _ = || {};
|
|||
| ~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:146:32
|
||||
--> tests/ui/needless_return.rs:150:32
|
||||
|
|
||||
LL | res.unwrap_or_else(|_| return Foo)
|
||||
| ^^^^^^^^^^
|
||||
|
|
@ -284,18 +284,6 @@ help: remove `return`
|
|||
LL | res.unwrap_or_else(|_| Foo)
|
||||
| ~~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:155:5
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return true;
|
||||
LL + true
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:159:5
|
||||
|
|
||||
|
|
@ -309,7 +297,19 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:164:9
|
||||
--> tests/ui/needless_return.rs:163:5
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: remove `return`
|
||||
|
|
||||
LL - return true;
|
||||
LL + true
|
||||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:168:9
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -321,7 +321,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:166:9
|
||||
--> tests/ui/needless_return.rs:170:9
|
||||
|
|
||||
LL | return false;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -333,7 +333,7 @@ LL + false
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:172:17
|
||||
--> tests/ui/needless_return.rs:176:17
|
||||
|
|
||||
LL | true => return false,
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -344,7 +344,7 @@ LL | true => false,
|
|||
| ~~~~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:174:13
|
||||
--> tests/ui/needless_return.rs:178:13
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -356,7 +356,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:181:9
|
||||
--> tests/ui/needless_return.rs:185:9
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -368,7 +368,7 @@ LL + true
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:183:16
|
||||
--> tests/ui/needless_return.rs:187:16
|
||||
|
|
||||
LL | let _ = || return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -379,7 +379,7 @@ LL | let _ = || true;
|
|||
| ~~~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:187:5
|
||||
--> tests/ui/needless_return.rs:191:5
|
||||
|
|
||||
LL | return the_answer!();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -391,7 +391,7 @@ LL + the_answer!()
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:190:33
|
||||
--> tests/ui/needless_return.rs:194:33
|
||||
|
|
||||
LL | async fn async_test_void_fun() {
|
||||
| _________________________________^
|
||||
|
|
@ -406,7 +406,7 @@ LL + async fn async_test_void_fun() {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:195:11
|
||||
--> tests/ui/needless_return.rs:199:11
|
||||
|
|
||||
LL | if b {
|
||||
| ___________^
|
||||
|
|
@ -421,7 +421,7 @@ LL + if b {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:197:13
|
||||
--> tests/ui/needless_return.rs:201:13
|
||||
|
|
||||
LL | } else {
|
||||
| _____________^
|
||||
|
|
@ -436,7 +436,7 @@ LL + } else {
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:205:14
|
||||
--> tests/ui/needless_return.rs:209:14
|
||||
|
|
||||
LL | _ => return,
|
||||
| ^^^^^^
|
||||
|
|
@ -447,7 +447,7 @@ LL | _ => (),
|
|||
| ~~
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:218:9
|
||||
--> tests/ui/needless_return.rs:222:9
|
||||
|
|
||||
LL | return String::from("test");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -459,7 +459,7 @@ LL + String::from("test")
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:220:9
|
||||
--> tests/ui/needless_return.rs:224:9
|
||||
|
|
||||
LL | return String::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -471,7 +471,7 @@ LL + String::new()
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:236:5
|
||||
--> tests/ui/needless_return.rs:240:5
|
||||
|
|
||||
LL | return format!("Hello {}", "world!");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -483,7 +483,7 @@ LL + format!("Hello {}", "world!")
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:277:9
|
||||
--> tests/ui/needless_return.rs:281:9
|
||||
|
|
||||
LL | return true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
@ -497,7 +497,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:279:9
|
||||
--> tests/ui/needless_return.rs:283:9
|
||||
|
|
||||
LL | return false;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -509,7 +509,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:286:13
|
||||
--> tests/ui/needless_return.rs:290:13
|
||||
|
|
||||
LL | return 10;
|
||||
| ^^^^^^^^^
|
||||
|
|
@ -524,7 +524,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:289:13
|
||||
--> tests/ui/needless_return.rs:293:13
|
||||
|
|
||||
LL | return 100;
|
||||
| ^^^^^^^^^^
|
||||
|
|
@ -537,7 +537,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:297:9
|
||||
--> tests/ui/needless_return.rs:301:9
|
||||
|
|
||||
LL | return 0;
|
||||
| ^^^^^^^^
|
||||
|
|
@ -549,7 +549,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:304:13
|
||||
--> tests/ui/needless_return.rs:308:13
|
||||
|
|
||||
LL | return *(x as *const isize);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -564,7 +564,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:306:13
|
||||
--> tests/ui/needless_return.rs:310:13
|
||||
|
|
||||
LL | return !*(x as *const isize);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -577,7 +577,7 @@ LL ~ }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:313:20
|
||||
--> tests/ui/needless_return.rs:317:20
|
||||
|
|
||||
LL | let _ = 42;
|
||||
| ____________________^
|
||||
|
|
@ -594,7 +594,7 @@ LL + let _ = 42;
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:320:20
|
||||
--> tests/ui/needless_return.rs:324:20
|
||||
|
|
||||
LL | let _ = 42; return;
|
||||
| ^^^^^^^
|
||||
|
|
@ -606,7 +606,7 @@ LL + let _ = 42;
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:332:9
|
||||
--> tests/ui/needless_return.rs:336:9
|
||||
|
|
||||
LL | return Ok(format!("ok!"));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -618,7 +618,7 @@ LL + Ok(format!("ok!"))
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:334:9
|
||||
--> tests/ui/needless_return.rs:338:9
|
||||
|
|
||||
LL | return Err(format!("err!"));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -630,7 +630,7 @@ LL + Err(format!("err!"))
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:340:9
|
||||
--> tests/ui/needless_return.rs:344:9
|
||||
|
|
||||
LL | return if true { 1 } else { 2 };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -642,7 +642,7 @@ LL + if true { 1 } else { 2 }
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:344:9
|
||||
--> tests/ui/needless_return.rs:348:9
|
||||
|
|
||||
LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -654,7 +654,7 @@ LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:365:5
|
||||
--> tests/ui/needless_return.rs:369:5
|
||||
|
|
||||
LL | return { "a".to_string() } + "b" + { "c" };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -666,7 +666,7 @@ LL + ({ "a".to_string() } + "b" + { "c" })
|
|||
|
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/needless_return.rs:369:5
|
||||
--> tests/ui/needless_return.rs:373:5
|
||||
|
|
||||
LL | return "".split("").next().unwrap().to_string();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![warn(clippy::transmute_float_to_int)]
|
||||
#![allow(clippy::missing_transmute_annotations)]
|
||||
#![feature(f128, f128_const)]
|
||||
#![feature(f16, f16_const)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
|
||||
fn float_to_int() {
|
||||
let _: u32 = unsafe { 1f32.to_bits() };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![warn(clippy::transmute_float_to_int)]
|
||||
#![allow(clippy::missing_transmute_annotations)]
|
||||
#![feature(f128, f128_const)]
|
||||
#![feature(f16, f16_const)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
|
||||
fn float_to_int() {
|
||||
let _: u32 = unsafe { std::mem::transmute(1f32) };
|
||||
|
|
|
|||
|
|
@ -84,8 +84,11 @@ fn issue_10449() {
|
|||
}
|
||||
|
||||
// Pointers cannot be cast to integers in const contexts
|
||||
#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")]
|
||||
const fn issue_12402<P>(ptr: *const P) {
|
||||
unsafe { transmute::<*const i32, usize>(&42i32) };
|
||||
unsafe { transmute::<fn(*const P), usize>(issue_12402) };
|
||||
let _ = unsafe { transmute::<_, usize>(ptr) };
|
||||
// This test exists even though the compiler lints against it
|
||||
// to test that clippy's transmute lints do not trigger on this.
|
||||
unsafe { std::mem::transmute::<*const i32, usize>(&42i32) };
|
||||
unsafe { std::mem::transmute::<fn(*const P), usize>(issue_12402) };
|
||||
let _ = unsafe { std::mem::transmute::<_, usize>(ptr) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,8 +84,11 @@ fn issue_10449() {
|
|||
}
|
||||
|
||||
// Pointers cannot be cast to integers in const contexts
|
||||
#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")]
|
||||
const fn issue_12402<P>(ptr: *const P) {
|
||||
unsafe { transmute::<*const i32, usize>(&42i32) };
|
||||
unsafe { transmute::<fn(*const P), usize>(issue_12402) };
|
||||
let _ = unsafe { transmute::<_, usize>(ptr) };
|
||||
// This test exists even though the compiler lints against it
|
||||
// to test that clippy's transmute lints do not trigger on this.
|
||||
unsafe { std::mem::transmute::<*const i32, usize>(&42i32) };
|
||||
unsafe { std::mem::transmute::<fn(*const P), usize>(issue_12402) };
|
||||
let _ = unsafe { std::mem::transmute::<_, usize>(ptr) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,11 +42,14 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"ignore-cdb",
|
||||
"ignore-compare-mode-next-solver",
|
||||
"ignore-compare-mode-polonius",
|
||||
"ignore-coverage-map",
|
||||
"ignore-coverage-run",
|
||||
"ignore-cross-compile",
|
||||
"ignore-debug",
|
||||
"ignore-eabi",
|
||||
"ignore-emscripten",
|
||||
"ignore-endian-big",
|
||||
"ignore-enzyme",
|
||||
"ignore-freebsd",
|
||||
"ignore-fuchsia",
|
||||
"ignore-gdb",
|
||||
|
|
@ -64,23 +67,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"ignore-loongarch64",
|
||||
"ignore-macabi",
|
||||
"ignore-macos",
|
||||
"ignore-mode-assembly",
|
||||
"ignore-mode-codegen",
|
||||
"ignore-mode-codegen-units",
|
||||
"ignore-mode-coverage-map",
|
||||
"ignore-mode-coverage-run",
|
||||
"ignore-mode-crashes",
|
||||
"ignore-mode-debuginfo",
|
||||
"ignore-mode-incremental",
|
||||
"ignore-mode-js-doc-test",
|
||||
"ignore-mode-mir-opt",
|
||||
"ignore-mode-pretty",
|
||||
"ignore-mode-run-make",
|
||||
"ignore-mode-run-pass-valgrind",
|
||||
"ignore-mode-rustdoc",
|
||||
"ignore-mode-rustdoc-json",
|
||||
"ignore-mode-ui",
|
||||
"ignore-mode-ui-fulldeps",
|
||||
"ignore-msp430",
|
||||
"ignore-msvc",
|
||||
"ignore-musl",
|
||||
|
|
@ -144,7 +130,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"needs-git-hash",
|
||||
"needs-llvm-components",
|
||||
"needs-llvm-zstd",
|
||||
"needs-profiler-support",
|
||||
"needs-profiler-runtime",
|
||||
"needs-relocation-model-pic",
|
||||
"needs-run-enabled",
|
||||
"needs-rust-lld",
|
||||
|
|
@ -223,6 +209,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"pretty-compare-only",
|
||||
"pretty-expanded",
|
||||
"pretty-mode",
|
||||
"reference",
|
||||
"regex-error-pattern",
|
||||
"remap-src-base",
|
||||
"revisions",
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ macro_rules! string_enum {
|
|||
string_enum! {
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum Mode {
|
||||
RunPassValgrind => "run-pass-valgrind",
|
||||
Pretty => "pretty",
|
||||
DebugInfo => "debuginfo",
|
||||
Codegen => "codegen",
|
||||
|
|
@ -207,13 +206,6 @@ pub struct Config {
|
|||
/// Path to LLVM's bin directory.
|
||||
pub llvm_bin_dir: Option<PathBuf>,
|
||||
|
||||
/// The valgrind path.
|
||||
pub valgrind_path: Option<String>,
|
||||
|
||||
/// Whether to fail if we can't run run-pass-valgrind tests under valgrind
|
||||
/// (or, alternatively, to silently run them like regular run-pass tests).
|
||||
pub force_valgrind: bool,
|
||||
|
||||
/// The path to the Clang executable to run Clang-based tests with. If
|
||||
/// `None` then these tests will be ignored.
|
||||
pub run_clang_based_tests_with: Option<String>,
|
||||
|
|
@ -393,8 +385,8 @@ pub struct Config {
|
|||
pub git_merge_commit_email: String,
|
||||
|
||||
/// True if the profiler runtime is enabled for this target.
|
||||
/// Used by the "needs-profiler-support" header in test files.
|
||||
pub profiler_support: bool,
|
||||
/// Used by the "needs-profiler-runtime" directive in test files.
|
||||
pub profiler_runtime: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
|
|
|||
272
src/tools/compiletest/src/debuggers.rs
Normal file
272
src/tools/compiletest/src/debuggers.rs
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::common::{Config, Debugger};
|
||||
|
||||
pub(crate) fn configure_cdb(config: &Config) -> Option<Arc<Config>> {
|
||||
config.cdb.as_ref()?;
|
||||
|
||||
Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() }))
|
||||
}
|
||||
|
||||
pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
|
||||
config.gdb_version?;
|
||||
|
||||
if config.matches_env("msvc") {
|
||||
return None;
|
||||
}
|
||||
|
||||
if config.remote_test_client.is_some() && !config.target.contains("android") {
|
||||
println!(
|
||||
"WARNING: debuginfo tests are not available when \
|
||||
testing with remote"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
if config.target.contains("android") {
|
||||
println!(
|
||||
"{} debug-info test uses tcp 5039 port.\
|
||||
please reserve it",
|
||||
config.target
|
||||
);
|
||||
|
||||
// android debug-info test uses remote debugger so, we test 1 thread
|
||||
// at once as they're all sharing the same TCP port to communicate
|
||||
// over.
|
||||
//
|
||||
// we should figure out how to lift this restriction! (run them all
|
||||
// on different ports allocated dynamically).
|
||||
env::set_var("RUST_TEST_THREADS", "1");
|
||||
}
|
||||
|
||||
Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
|
||||
}
|
||||
|
||||
pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
|
||||
config.lldb_python_dir.as_ref()?;
|
||||
|
||||
if let Some(350) = config.lldb_version {
|
||||
println!(
|
||||
"WARNING: The used version of LLDB (350) has a \
|
||||
known issue that breaks debuginfo tests. See \
|
||||
issue #32520 for more information. Skipping all \
|
||||
LLDB-based tests!",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
|
||||
}
|
||||
|
||||
/// Returns `true` if the given target is an Android target for the
|
||||
/// purposes of GDB testing.
|
||||
pub(crate) fn is_android_gdb_target(target: &str) -> bool {
|
||||
matches!(
|
||||
&target[..],
|
||||
"arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing.
|
||||
fn is_pc_windows_msvc_target(target: &str) -> bool {
|
||||
target.ends_with("-pc-windows-msvc")
|
||||
}
|
||||
|
||||
fn find_cdb(target: &str) -> Option<OsString> {
|
||||
if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
|
||||
let cdb_arch = if cfg!(target_arch = "x86") {
|
||||
"x86"
|
||||
} else if cfg!(target_arch = "x86_64") {
|
||||
"x64"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"arm64"
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
"arm"
|
||||
} else {
|
||||
return None; // No compatible CDB.exe in the Windows 10 SDK
|
||||
};
|
||||
|
||||
let mut path = PathBuf::new();
|
||||
path.push(pf86);
|
||||
path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
|
||||
path.push(cdb_arch);
|
||||
path.push(r"cdb.exe");
|
||||
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(path.into_os_string())
|
||||
}
|
||||
|
||||
/// Returns Path to CDB
|
||||
pub(crate) fn analyze_cdb(
|
||||
cdb: Option<String>,
|
||||
target: &str,
|
||||
) -> (Option<OsString>, Option<[u16; 4]>) {
|
||||
let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
|
||||
|
||||
let mut version = None;
|
||||
if let Some(cdb) = cdb.as_ref() {
|
||||
if let Ok(output) = Command::new(cdb).arg("/version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
version = extract_cdb_version(&first_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(cdb, version)
|
||||
}
|
||||
|
||||
pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
|
||||
// Example full_version_line: "cdb version 10.0.18362.1"
|
||||
let version = full_version_line.rsplit(' ').next()?;
|
||||
let mut components = version.split('.');
|
||||
let major: u16 = components.next().unwrap().parse().unwrap();
|
||||
let minor: u16 = components.next().unwrap().parse().unwrap();
|
||||
let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
|
||||
let build: u16 = components.next().unwrap_or("0").parse().unwrap();
|
||||
Some([major, minor, patch, build])
|
||||
}
|
||||
|
||||
/// Returns (Path to GDB, GDB Version)
|
||||
pub(crate) fn analyze_gdb(
|
||||
gdb: Option<String>,
|
||||
target: &str,
|
||||
android_cross_path: &PathBuf,
|
||||
) -> (Option<String>, Option<u32>) {
|
||||
#[cfg(not(windows))]
|
||||
const GDB_FALLBACK: &str = "gdb";
|
||||
#[cfg(windows)]
|
||||
const GDB_FALLBACK: &str = "gdb.exe";
|
||||
|
||||
let fallback_gdb = || {
|
||||
if is_android_gdb_target(target) {
|
||||
let mut gdb_path = match android_cross_path.to_str() {
|
||||
Some(x) => x.to_owned(),
|
||||
None => panic!("cannot find android cross path"),
|
||||
};
|
||||
gdb_path.push_str("/bin/gdb");
|
||||
gdb_path
|
||||
} else {
|
||||
GDB_FALLBACK.to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
let gdb = match gdb {
|
||||
None => fallback_gdb(),
|
||||
Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
|
||||
Some(ref s) => s.to_owned(),
|
||||
};
|
||||
|
||||
let mut version_line = None;
|
||||
if let Ok(output) = Command::new(&gdb).arg("--version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
version_line = Some(first_line.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let version = match version_line {
|
||||
Some(line) => extract_gdb_version(&line),
|
||||
None => return (None, None),
|
||||
};
|
||||
|
||||
(Some(gdb), version)
|
||||
}
|
||||
|
||||
pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
|
||||
let full_version_line = full_version_line.trim();
|
||||
|
||||
// GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
|
||||
// of the ? sections being optional
|
||||
|
||||
// We will parse up to 3 digits for each component, ignoring the date
|
||||
|
||||
// We skip text in parentheses. This avoids accidentally parsing
|
||||
// the openSUSE version, which looks like:
|
||||
// GNU gdb (GDB; openSUSE Leap 15.0) 8.1
|
||||
// This particular form is documented in the GNU coding standards:
|
||||
// https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
|
||||
|
||||
let unbracketed_part = full_version_line.split('[').next().unwrap();
|
||||
let mut splits = unbracketed_part.trim_end().rsplit(' ');
|
||||
let version_string = splits.next().unwrap();
|
||||
|
||||
let mut splits = version_string.split('.');
|
||||
let major = splits.next().unwrap();
|
||||
let minor = splits.next().unwrap();
|
||||
let patch = splits.next();
|
||||
|
||||
let major: u32 = major.parse().unwrap();
|
||||
let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
|
||||
None => {
|
||||
let minor = minor.parse().unwrap();
|
||||
let patch: u32 = match patch {
|
||||
Some(patch) => match patch.find(not_a_digit) {
|
||||
None => patch.parse().unwrap(),
|
||||
Some(idx) if idx > 3 => 0,
|
||||
Some(idx) => patch[..idx].parse().unwrap(),
|
||||
},
|
||||
None => 0,
|
||||
};
|
||||
(minor, patch)
|
||||
}
|
||||
// There is no patch version after minor-date (e.g. "4-2012").
|
||||
Some(idx) => {
|
||||
let minor = minor[..idx].parse().unwrap();
|
||||
(minor, 0)
|
||||
}
|
||||
};
|
||||
|
||||
Some(((major * 1000) + minor) * 1000 + patch)
|
||||
}
|
||||
|
||||
/// Returns LLDB version
|
||||
pub(crate) fn extract_lldb_version(full_version_line: &str) -> Option<u32> {
|
||||
// Extract the major LLDB version from the given version string.
|
||||
// LLDB version strings are different for Apple and non-Apple platforms.
|
||||
// The Apple variant looks like this:
|
||||
//
|
||||
// LLDB-179.5 (older versions)
|
||||
// lldb-300.2.51 (new versions)
|
||||
//
|
||||
// We are only interested in the major version number, so this function
|
||||
// will return `Some(179)` and `Some(300)` respectively.
|
||||
//
|
||||
// Upstream versions look like:
|
||||
// lldb version 6.0.1
|
||||
//
|
||||
// There doesn't seem to be a way to correlate the Apple version
|
||||
// with the upstream version, and since the tests were originally
|
||||
// written against Apple versions, we make a fake Apple version by
|
||||
// multiplying the first number by 100. This is a hack.
|
||||
|
||||
let full_version_line = full_version_line.trim();
|
||||
|
||||
if let Some(apple_ver) =
|
||||
full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
|
||||
{
|
||||
if let Some(idx) = apple_ver.find(not_a_digit) {
|
||||
let version: u32 = apple_ver[..idx].parse().unwrap();
|
||||
return Some(version);
|
||||
}
|
||||
} else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
|
||||
if let Some(idx) = lldb_ver.find(not_a_digit) {
|
||||
let version: u32 = lldb_ver[..idx].parse().ok()?;
|
||||
return Some(version * 100);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn not_a_digit(c: char) -> bool {
|
||||
!c.is_ascii_digit()
|
||||
}
|
||||
|
|
@ -5,17 +5,17 @@ use std::io::BufReader;
|
|||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use regex::Regex;
|
||||
use tracing::*;
|
||||
|
||||
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
|
||||
use crate::debuggers::{extract_cdb_version, extract_gdb_version};
|
||||
use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
|
||||
use crate::header::cfg::{MatchOutcome, parse_cfg_name_directive};
|
||||
use crate::header::needs::CachedNeedsConditions;
|
||||
use crate::util::static_regex;
|
||||
use crate::{extract_cdb_version, extract_gdb_version};
|
||||
|
||||
pub(crate) mod auxiliary;
|
||||
mod cfg;
|
||||
mod needs;
|
||||
#[cfg(test)]
|
||||
|
|
@ -35,9 +35,10 @@ impl HeadersCache {
|
|||
/// the test.
|
||||
#[derive(Default)]
|
||||
pub struct EarlyProps {
|
||||
pub aux: Vec<String>,
|
||||
pub aux_bin: Vec<String>,
|
||||
pub aux_crate: Vec<(String, String)>,
|
||||
/// Auxiliary crates that should be built and made available to this test.
|
||||
/// Included in [`EarlyProps`] so that the indicated files can participate
|
||||
/// in up-to-date checking. Building happens via [`TestProps::aux`] instead.
|
||||
pub(crate) aux: AuxProps,
|
||||
pub revisions: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
@ -56,23 +57,9 @@ impl EarlyProps {
|
|||
&mut poisoned,
|
||||
testfile,
|
||||
rdr,
|
||||
&mut |HeaderLine { directive: ln, .. }| {
|
||||
config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| {
|
||||
r.trim().to_string()
|
||||
});
|
||||
config.push_name_value_directive(
|
||||
ln,
|
||||
directives::AUX_BIN,
|
||||
&mut props.aux_bin,
|
||||
|r| r.trim().to_string(),
|
||||
);
|
||||
config.push_name_value_directive(
|
||||
ln,
|
||||
directives::AUX_CRATE,
|
||||
&mut props.aux_crate,
|
||||
Config::parse_aux_crate,
|
||||
);
|
||||
config.parse_and_update_revisions(ln, &mut props.revisions);
|
||||
&mut |DirectiveLine { directive: ln, .. }| {
|
||||
parse_and_update_aux(config, ln, &mut props.aux);
|
||||
config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -100,18 +87,8 @@ pub struct TestProps {
|
|||
// If present, the name of a file that this test should match when
|
||||
// pretty-printed
|
||||
pub pp_exact: Option<PathBuf>,
|
||||
// Other crates that should be compiled (typically from the same
|
||||
// directory as the test, but for backwards compatibility reasons
|
||||
// we also check the auxiliary directory)
|
||||
pub aux_builds: Vec<String>,
|
||||
// Auxiliary crates that should be compiled as `#![crate_type = "bin"]`.
|
||||
pub aux_bins: Vec<String>,
|
||||
// Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies
|
||||
// to build and pass with the `--extern` flag.
|
||||
pub aux_crates: Vec<(String, String)>,
|
||||
/// Similar to `aux_builds`, but also passes the resulting dylib path to
|
||||
/// `-Zcodegen-backend`.
|
||||
pub aux_codegen_backend: Option<String>,
|
||||
/// Auxiliary crates that should be built and made available to this test.
|
||||
pub(crate) aux: AuxProps,
|
||||
// Environment settings to use for compiling
|
||||
pub rustc_env: Vec<(String, String)>,
|
||||
// Environment variables to unset prior to compiling.
|
||||
|
|
@ -278,10 +255,7 @@ impl TestProps {
|
|||
run_flags: vec![],
|
||||
doc_flags: vec![],
|
||||
pp_exact: None,
|
||||
aux_builds: vec![],
|
||||
aux_bins: vec![],
|
||||
aux_crates: vec![],
|
||||
aux_codegen_backend: None,
|
||||
aux: Default::default(),
|
||||
revisions: vec![],
|
||||
rustc_env: vec![
|
||||
("RUSTC_ICE".to_string(), "0".to_string()),
|
||||
|
|
@ -370,7 +344,7 @@ impl TestProps {
|
|||
&mut poisoned,
|
||||
testfile,
|
||||
file,
|
||||
&mut |HeaderLine { header_revision, directive: ln, .. }| {
|
||||
&mut |DirectiveLine { header_revision, directive: ln, .. }| {
|
||||
if header_revision.is_some() && header_revision != test_revision {
|
||||
return;
|
||||
}
|
||||
|
|
@ -417,7 +391,7 @@ impl TestProps {
|
|||
has_edition = true;
|
||||
}
|
||||
|
||||
config.parse_and_update_revisions(ln, &mut self.revisions);
|
||||
config.parse_and_update_revisions(testfile, ln, &mut self.revisions);
|
||||
|
||||
if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) {
|
||||
self.run_flags.extend(split_flags(&flags));
|
||||
|
|
@ -456,21 +430,10 @@ impl TestProps {
|
|||
PRETTY_COMPARE_ONLY,
|
||||
&mut self.pretty_compare_only,
|
||||
);
|
||||
config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| {
|
||||
r.trim().to_string()
|
||||
});
|
||||
config.push_name_value_directive(ln, AUX_BIN, &mut self.aux_bins, |r| {
|
||||
r.trim().to_string()
|
||||
});
|
||||
config.push_name_value_directive(
|
||||
ln,
|
||||
AUX_CRATE,
|
||||
&mut self.aux_crates,
|
||||
Config::parse_aux_crate,
|
||||
);
|
||||
if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
|
||||
self.aux_codegen_backend = Some(r.trim().to_owned());
|
||||
}
|
||||
|
||||
// Call a helper method to deal with aux-related directives.
|
||||
parse_and_update_aux(config, ln, &mut self.aux);
|
||||
|
||||
config.push_name_value_directive(
|
||||
ln,
|
||||
EXEC_ENV,
|
||||
|
|
@ -717,7 +680,7 @@ impl TestProps {
|
|||
|
||||
/// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present.
|
||||
///
|
||||
/// See [`HeaderLine`] for a diagram.
|
||||
/// See [`DirectiveLine`] for a diagram.
|
||||
pub fn line_directive<'line>(
|
||||
comment: &str,
|
||||
original_line: &'line str,
|
||||
|
|
@ -775,17 +738,13 @@ const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] =
|
|||
/// ```text
|
||||
/// //@ compile-flags: -O
|
||||
/// ^^^^^^^^^^^^^^^^^ directive
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^ original_line
|
||||
///
|
||||
/// //@ [foo] compile-flags: -O
|
||||
/// ^^^ header_revision
|
||||
/// ^^^^^^^^^^^^^^^^^ directive
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ original_line
|
||||
/// ```
|
||||
struct HeaderLine<'ln> {
|
||||
struct DirectiveLine<'ln> {
|
||||
line_number: usize,
|
||||
/// Raw line from the test file, including comment prefix and any revision.
|
||||
original_line: &'ln str,
|
||||
/// Some header directives start with a revision name in square brackets
|
||||
/// (e.g. `[foo]`), and only apply to that revision of the test.
|
||||
/// If present, this field contains the revision name (e.g. `foo`).
|
||||
|
|
@ -797,7 +756,6 @@ struct HeaderLine<'ln> {
|
|||
|
||||
pub(crate) struct CheckDirectiveResult<'ln> {
|
||||
is_known_directive: bool,
|
||||
directive_name: &'ln str,
|
||||
trailing_directive: Option<&'ln str>,
|
||||
}
|
||||
|
||||
|
|
@ -832,11 +790,7 @@ pub(crate) fn check_directive<'a>(
|
|||
}
|
||||
.then_some(trailing);
|
||||
|
||||
CheckDirectiveResult {
|
||||
is_known_directive: is_known(&directive_name),
|
||||
directive_name: directive_ln,
|
||||
trailing_directive,
|
||||
}
|
||||
CheckDirectiveResult { is_known_directive: is_known(&directive_name), trailing_directive }
|
||||
}
|
||||
|
||||
fn iter_header(
|
||||
|
|
@ -845,150 +799,119 @@ fn iter_header(
|
|||
poisoned: &mut bool,
|
||||
testfile: &Path,
|
||||
rdr: impl Read,
|
||||
it: &mut dyn FnMut(HeaderLine<'_>),
|
||||
it: &mut dyn FnMut(DirectiveLine<'_>),
|
||||
) {
|
||||
if testfile.is_dir() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Coverage tests in coverage-run mode always have these extra directives,
|
||||
// without needing to specify them manually in every test file.
|
||||
// (Some of the comments below have been copied over from the old
|
||||
// `tests/run-make/coverage-reports/Makefile`, which no longer exists.)
|
||||
// Coverage tests in coverage-run mode always have these extra directives, without needing to
|
||||
// specify them manually in every test file. (Some of the comments below have been copied over
|
||||
// from the old `tests/run-make/coverage-reports/Makefile`, which no longer exists.)
|
||||
//
|
||||
// FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later.
|
||||
if mode == Mode::CoverageRun {
|
||||
let extra_directives: &[&str] = &[
|
||||
"needs-profiler-support",
|
||||
// FIXME(pietroalbini): this test currently does not work on cross-compiled
|
||||
// targets because remote-test is not capable of sending back the *.profraw
|
||||
// files generated by the LLVM instrumentation.
|
||||
"needs-profiler-runtime",
|
||||
// FIXME(pietroalbini): this test currently does not work on cross-compiled targets
|
||||
// because remote-test is not capable of sending back the *.profraw files generated by
|
||||
// the LLVM instrumentation.
|
||||
"ignore-cross-compile",
|
||||
];
|
||||
// Process the extra implied directives, with a dummy line number of 0.
|
||||
for directive in extra_directives {
|
||||
it(HeaderLine { line_number: 0, original_line: "", header_revision: None, directive });
|
||||
it(DirectiveLine { line_number: 0, header_revision: None, directive });
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(jieyouxu): once we get rid of `Makefile`s we can unconditionally check for `//@`.
|
||||
let comment = if testfile.extension().is_some_and(|e| e == "rs") { "//@" } else { "#" };
|
||||
|
||||
let mut rdr = BufReader::with_capacity(1024, rdr);
|
||||
let mut ln = String::new();
|
||||
let mut line_number = 0;
|
||||
|
||||
// Match on error annotations like `//~ERROR`.
|
||||
static REVISION_MAGIC_COMMENT_RE: OnceLock<Regex> = OnceLock::new();
|
||||
let revision_magic_comment_re =
|
||||
REVISION_MAGIC_COMMENT_RE.get_or_init(|| Regex::new("//(\\[.*\\])?~.*").unwrap());
|
||||
|
||||
loop {
|
||||
line_number += 1;
|
||||
ln.clear();
|
||||
if rdr.read_line(&mut ln).unwrap() == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
// Assume that any directives will be found before the first
|
||||
// module or function. This doesn't seem to be an optimization
|
||||
// with a warm page cache. Maybe with a cold one.
|
||||
let original_line = &ln;
|
||||
let ln = ln.trim();
|
||||
|
||||
// Assume that any directives will be found before the first module or function. This
|
||||
// doesn't seem to be an optimization with a warm page cache. Maybe with a cold one.
|
||||
// FIXME(jieyouxu): this will cause `//@` directives in the rest of the test file to
|
||||
// not be checked.
|
||||
if ln.starts_with("fn") || ln.starts_with("mod") {
|
||||
return;
|
||||
}
|
||||
|
||||
// First try to accept `ui_test` style comments (`//@`)
|
||||
} else if let Some((header_revision, non_revisioned_directive_line)) =
|
||||
line_directive(comment, ln)
|
||||
{
|
||||
// Perform unknown directive check on Rust files.
|
||||
if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
|
||||
let directive_ln = non_revisioned_directive_line.trim();
|
||||
let Some((header_revision, non_revisioned_directive_line)) = line_directive(comment, ln)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let CheckDirectiveResult { is_known_directive, trailing_directive, .. } =
|
||||
check_directive(directive_ln, mode, ln);
|
||||
// Perform unknown directive check on Rust files.
|
||||
if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
|
||||
let directive_ln = non_revisioned_directive_line.trim();
|
||||
|
||||
if !is_known_directive {
|
||||
*poisoned = true;
|
||||
let CheckDirectiveResult { is_known_directive, trailing_directive } =
|
||||
check_directive(directive_ln, mode, ln);
|
||||
|
||||
eprintln!(
|
||||
"error: detected unknown compiletest test directive `{}` in {}:{}",
|
||||
directive_ln,
|
||||
testfile.display(),
|
||||
line_number,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(trailing_directive) = &trailing_directive {
|
||||
*poisoned = true;
|
||||
|
||||
eprintln!(
|
||||
"error: detected trailing compiletest test directive `{}` in {}:{}\n \
|
||||
help: put the trailing directive in it's own line: `//@ {}`",
|
||||
trailing_directive,
|
||||
testfile.display(),
|
||||
line_number,
|
||||
trailing_directive,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
it(HeaderLine {
|
||||
line_number,
|
||||
original_line,
|
||||
header_revision,
|
||||
directive: non_revisioned_directive_line,
|
||||
});
|
||||
// Then we try to check for legacy-style candidates, which are not the magic ~ERROR family
|
||||
// error annotations.
|
||||
} else if !revision_magic_comment_re.is_match(ln) {
|
||||
let Some((_, rest)) = line_directive("//", ln) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if rest.trim_start().starts_with(':') {
|
||||
// This is likely a markdown link:
|
||||
// `[link_name]: https://example.org`
|
||||
continue;
|
||||
}
|
||||
|
||||
let rest = rest.trim_start();
|
||||
|
||||
let CheckDirectiveResult { is_known_directive, directive_name, .. } =
|
||||
check_directive(rest, mode, ln);
|
||||
|
||||
if is_known_directive {
|
||||
if !is_known_directive {
|
||||
*poisoned = true;
|
||||
|
||||
eprintln!(
|
||||
"error: detected legacy-style directive {} in compiletest test: {}:{}, please use `ui_test`-style directives `//@` instead: {:#?}",
|
||||
directive_name,
|
||||
"error: detected unknown compiletest test directive `{}` in {}:{}",
|
||||
directive_ln,
|
||||
testfile.display(),
|
||||
line_number,
|
||||
line_directive("//", ln),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(trailing_directive) = &trailing_directive {
|
||||
*poisoned = true;
|
||||
|
||||
eprintln!(
|
||||
"error: detected trailing compiletest test directive `{}` in {}:{}\n \
|
||||
help: put the trailing directive in it's own line: `//@ {}`",
|
||||
trailing_directive,
|
||||
testfile.display(),
|
||||
line_number,
|
||||
trailing_directive,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
it(DirectiveLine {
|
||||
line_number,
|
||||
header_revision,
|
||||
directive: non_revisioned_directive_line,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn parse_aux_crate(r: String) -> (String, String) {
|
||||
let mut parts = r.trim().splitn(2, '=');
|
||||
(
|
||||
parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
|
||||
parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) {
|
||||
fn parse_and_update_revisions(&self, testfile: &Path, line: &str, existing: &mut Vec<String>) {
|
||||
if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
|
||||
if self.mode == Mode::RunMake {
|
||||
panic!("`run-make` tests do not support revisions: {}", testfile.display());
|
||||
}
|
||||
|
||||
let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
|
||||
for revision in raw.split_whitespace().map(|r| r.to_string()) {
|
||||
if !duplicates.insert(revision.clone()) {
|
||||
panic!("Duplicate revision: `{}` in line `{}`", revision, raw);
|
||||
panic!(
|
||||
"duplicate revision: `{}` in line `{}`: {}",
|
||||
revision,
|
||||
raw,
|
||||
testfile.display()
|
||||
);
|
||||
}
|
||||
existing.push(revision);
|
||||
}
|
||||
|
|
@ -1362,13 +1285,14 @@ pub fn make_test_description<R: Read>(
|
|||
|
||||
let mut local_poisoned = false;
|
||||
|
||||
// Scan through the test file to handle `ignore-*`, `only-*`, and `needs-*` directives.
|
||||
iter_header(
|
||||
config.mode,
|
||||
&config.suite,
|
||||
&mut local_poisoned,
|
||||
path,
|
||||
src,
|
||||
&mut |HeaderLine { header_revision, original_line, directive: ln, line_number }| {
|
||||
&mut |DirectiveLine { header_revision, directive: ln, line_number }| {
|
||||
if header_revision.is_some() && header_revision != test_revision {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1393,17 +1317,7 @@ pub fn make_test_description<R: Read>(
|
|||
};
|
||||
}
|
||||
|
||||
if let Some((_, post)) = original_line.trim_start().split_once("//") {
|
||||
let post = post.trim_start();
|
||||
if post.starts_with("ignore-tidy") {
|
||||
// Not handled by compiletest.
|
||||
} else {
|
||||
decision!(cfg::handle_ignore(config, ln));
|
||||
}
|
||||
} else {
|
||||
decision!(cfg::handle_ignore(config, ln));
|
||||
}
|
||||
|
||||
decision!(cfg::handle_ignore(config, ln));
|
||||
decision!(cfg::handle_only(config, ln));
|
||||
decision!(needs::handle_needs(&cache.needs, config, ln));
|
||||
decision!(ignore_llvm(config, ln));
|
||||
|
|
|
|||
60
src/tools/compiletest/src/header/auxiliary.rs
Normal file
60
src/tools/compiletest/src/header/auxiliary.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//! Code for dealing with test directives that request an "auxiliary" crate to
|
||||
//! be built and made available to the test in some way.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use crate::common::Config;
|
||||
use crate::header::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE};
|
||||
|
||||
/// Properties parsed from `aux-*` test directives.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct AuxProps {
|
||||
/// Other crates that should be built and made available to this test.
|
||||
/// These are filenames relative to `./auxiliary/` in the test's directory.
|
||||
pub(crate) builds: Vec<String>,
|
||||
/// Auxiliary crates that should be compiled as `#![crate_type = "bin"]`.
|
||||
pub(crate) bins: Vec<String>,
|
||||
/// Similar to `builds`, but a list of NAME=somelib.rs of dependencies
|
||||
/// to build and pass with the `--extern` flag.
|
||||
pub(crate) crates: Vec<(String, String)>,
|
||||
/// Similar to `builds`, but also uses the resulting dylib as a
|
||||
/// `-Zcodegen-backend` when compiling the test file.
|
||||
pub(crate) codegen_backend: Option<String>,
|
||||
}
|
||||
|
||||
impl AuxProps {
|
||||
/// Yields all of the paths (relative to `./auxiliary/`) that have been
|
||||
/// specified in `aux-*` directives for this test.
|
||||
pub(crate) fn all_aux_path_strings(&self) -> impl Iterator<Item = &str> {
|
||||
let Self { builds, bins, crates, codegen_backend } = self;
|
||||
|
||||
iter::empty()
|
||||
.chain(builds.iter().map(String::as_str))
|
||||
.chain(bins.iter().map(String::as_str))
|
||||
.chain(crates.iter().map(|(_, path)| path.as_str()))
|
||||
.chain(codegen_backend.iter().map(String::as_str))
|
||||
}
|
||||
}
|
||||
|
||||
/// If the given test directive line contains an `aux-*` directive, parse it
|
||||
/// and update [`AuxProps`] accordingly.
|
||||
pub(super) fn parse_and_update_aux(config: &Config, ln: &str, aux: &mut AuxProps) {
|
||||
if !ln.starts_with("aux-") {
|
||||
return;
|
||||
}
|
||||
|
||||
config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string());
|
||||
config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string());
|
||||
config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate);
|
||||
if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
|
||||
aux.codegen_backend = Some(r.trim().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_aux_crate(r: String) -> (String, String) {
|
||||
let mut parts = r.trim().splitn(2, '=');
|
||||
(
|
||||
parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
|
||||
parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use crate::common::{CompareMode, Config, Debugger, Mode};
|
||||
use crate::common::{CompareMode, Config, Debugger};
|
||||
use crate::header::IgnoreDecision;
|
||||
|
||||
const EXTRA_ARCHS: &[&str] = &["spirv"];
|
||||
|
|
@ -166,6 +166,12 @@ pub(super) fn parse_cfg_name_directive<'a>(
|
|||
message: "when the target vendor is Apple"
|
||||
}
|
||||
|
||||
condition! {
|
||||
name: "enzyme",
|
||||
condition: config.has_enzyme,
|
||||
message: "when rustc is built with LLVM Enzyme"
|
||||
}
|
||||
|
||||
// Technically the locally built compiler uses the "dev" channel rather than the "nightly"
|
||||
// channel, even though most people don't know or won't care about it. To avoid confusion, we
|
||||
// treat the "dev" channel as the "nightly" channel when processing the directive.
|
||||
|
|
@ -217,13 +223,10 @@ pub(super) fn parse_cfg_name_directive<'a>(
|
|||
}
|
||||
// Coverage tests run the same test file in multiple modes.
|
||||
// If a particular test should not be run in one of the modes, ignore it
|
||||
// with "ignore-mode-coverage-map" or "ignore-mode-coverage-run".
|
||||
// with "ignore-coverage-map" or "ignore-coverage-run".
|
||||
condition! {
|
||||
name: format!("mode-{}", config.mode.to_str()),
|
||||
allowed_names: ContainsPrefixed {
|
||||
prefix: "mode-",
|
||||
inner: Mode::STR_VARIANTS,
|
||||
},
|
||||
name: config.mode.to_str(),
|
||||
allowed_names: ["coverage-map", "coverage-run"],
|
||||
message: "when the test mode is {name}",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,9 +100,9 @@ pub(super) fn handle_needs(
|
|||
ignore_reason: "ignored on targets without unwinding support",
|
||||
},
|
||||
Need {
|
||||
name: "needs-profiler-support",
|
||||
condition: cache.profiler_support,
|
||||
ignore_reason: "ignored when profiler support is disabled",
|
||||
name: "needs-profiler-runtime",
|
||||
condition: config.profiler_runtime,
|
||||
ignore_reason: "ignored when the profiler runtime is not available",
|
||||
},
|
||||
Need {
|
||||
name: "needs-force-clang-based-tests",
|
||||
|
|
@ -220,7 +220,6 @@ pub(super) struct CachedNeedsConditions {
|
|||
sanitizer_memtag: bool,
|
||||
sanitizer_shadow_call_stack: bool,
|
||||
sanitizer_safestack: bool,
|
||||
profiler_support: bool,
|
||||
xray: bool,
|
||||
rust_lld: bool,
|
||||
dlltool: bool,
|
||||
|
|
@ -247,7 +246,6 @@ impl CachedNeedsConditions {
|
|||
sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag),
|
||||
sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack),
|
||||
sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack),
|
||||
profiler_support: config.profiler_support,
|
||||
xray: config.target_cfg().xray,
|
||||
|
||||
// For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`),
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// ignore-wasm
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::iter_header;
|
||||
use crate::common::{Config, Debugger, Mode};
|
||||
|
|
@ -70,7 +69,7 @@ struct ConfigBuilder {
|
|||
llvm_version: Option<String>,
|
||||
git_hash: bool,
|
||||
system_llvm: bool,
|
||||
profiler_support: bool,
|
||||
profiler_runtime: bool,
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
|
|
@ -114,8 +113,8 @@ impl ConfigBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
fn profiler_support(&mut self, s: bool) -> &mut Self {
|
||||
self.profiler_support = s;
|
||||
fn profiler_runtime(&mut self, is_available: bool) -> &mut Self {
|
||||
self.profiler_runtime = is_available;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -163,8 +162,8 @@ impl ConfigBuilder {
|
|||
if self.system_llvm {
|
||||
args.push("--system-llvm".to_owned());
|
||||
}
|
||||
if self.profiler_support {
|
||||
args.push("--profiler-support".to_owned());
|
||||
if self.profiler_runtime {
|
||||
args.push("--profiler-runtime".to_owned());
|
||||
}
|
||||
|
||||
args.push("--rustc-path".to_string());
|
||||
|
|
@ -243,7 +242,8 @@ fn aux_build() {
|
|||
//@ aux-build: b.rs
|
||||
"
|
||||
)
|
||||
.aux,
|
||||
.aux
|
||||
.builds,
|
||||
vec!["a.rs", "b.rs"],
|
||||
);
|
||||
}
|
||||
|
|
@ -369,12 +369,12 @@ fn sanitizers() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn profiler_support() {
|
||||
let config: Config = cfg().profiler_support(false).build();
|
||||
assert!(check_ignore(&config, "//@ needs-profiler-support"));
|
||||
fn profiler_runtime() {
|
||||
let config: Config = cfg().profiler_runtime(false).build();
|
||||
assert!(check_ignore(&config, "//@ needs-profiler-runtime"));
|
||||
|
||||
let config: Config = cfg().profiler_support(true).build();
|
||||
assert!(!check_ignore(&config, "//@ needs-profiler-support"));
|
||||
let config: Config = cfg().profiler_runtime(true).build();
|
||||
assert!(!check_ignore(&config, "//@ needs-profiler-runtime"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -423,7 +423,7 @@ fn test_extract_version_range() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
|
||||
#[should_panic(expected = "duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
|
||||
fn test_duplicate_revisions() {
|
||||
let config: Config = cfg().build();
|
||||
parse_rs(&config, "//@ revisions: rpass1 rpass1");
|
||||
|
|
@ -573,19 +573,15 @@ fn families() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_mode() {
|
||||
for &mode in Mode::STR_VARIANTS {
|
||||
// Indicate profiler support so that "coverage-run" tests aren't skipped.
|
||||
let config: Config = cfg().mode(mode).profiler_support(true).build();
|
||||
let other = if mode == "coverage-run" { "coverage-map" } else { "coverage-run" };
|
||||
fn ignore_coverage() {
|
||||
// Indicate profiler runtime availability so that "coverage-run" tests aren't skipped.
|
||||
let config = cfg().mode("coverage-map").profiler_runtime(true).build();
|
||||
assert!(check_ignore(&config, "//@ ignore-coverage-map"));
|
||||
assert!(!check_ignore(&config, "//@ ignore-coverage-run"));
|
||||
|
||||
assert_ne!(mode, other);
|
||||
assert_eq!(config.mode, Mode::from_str(mode).unwrap());
|
||||
assert_ne!(config.mode, Mode::from_str(other).unwrap());
|
||||
|
||||
assert!(check_ignore(&config, &format!("//@ ignore-mode-{mode}")));
|
||||
assert!(!check_ignore(&config, &format!("//@ ignore-mode-{other}")));
|
||||
}
|
||||
let config = cfg().mode("coverage-run").profiler_runtime(true).build();
|
||||
assert!(!check_ignore(&config, "//@ ignore-coverage-map"));
|
||||
assert!(check_ignore(&config, "//@ ignore-coverage-run"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -621,17 +617,6 @@ fn test_unknown_directive_check() {
|
|||
assert!(poisoned);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_known_legacy_directive_check() {
|
||||
let mut poisoned = false;
|
||||
run_path(
|
||||
&mut poisoned,
|
||||
Path::new("a.rs"),
|
||||
include_bytes!("./test-auxillary/known_legacy_directive.rs"),
|
||||
);
|
||||
assert!(poisoned);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_known_directive_check_no_error() {
|
||||
let mut poisoned = false;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ mod tests;
|
|||
|
||||
pub mod common;
|
||||
pub mod compute_diff;
|
||||
mod debuggers;
|
||||
pub mod errors;
|
||||
pub mod header;
|
||||
mod json;
|
||||
|
|
@ -36,8 +37,8 @@ use walkdir::WalkDir;
|
|||
|
||||
use self::header::{EarlyProps, make_test_description};
|
||||
use crate::common::{
|
||||
Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
|
||||
output_base_dir, output_relative_path,
|
||||
Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir,
|
||||
output_relative_path,
|
||||
};
|
||||
use crate::header::HeadersCache;
|
||||
use crate::util::logv;
|
||||
|
|
@ -53,8 +54,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
.reqopt("", "python", "path to python to use for doc tests", "PATH")
|
||||
.optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
|
||||
.optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH")
|
||||
.optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
|
||||
.optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
|
||||
.optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
|
||||
.optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
|
||||
.reqopt("", "src-base", "directory to scan for test files", "PATH")
|
||||
|
|
@ -65,7 +64,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
"",
|
||||
"mode",
|
||||
"which sort of compile tests to run",
|
||||
"run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
|
||||
"pretty | debug-info | codegen | rustdoc \
|
||||
| rustdoc-json | codegen-units | incremental | run-make | ui \
|
||||
| js-doc-test | mir-opt | assembly | crashes",
|
||||
)
|
||||
|
|
@ -155,7 +154,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged")
|
||||
.optflag("", "only-modified", "only run tests that result been modified")
|
||||
.optflag("", "nocapture", "")
|
||||
.optflag("", "profiler-support", "is the profiler runtime enabled for this target")
|
||||
.optflag("", "profiler-runtime", "is the profiler runtime enabled for this target")
|
||||
.optflag("h", "help", "show this message")
|
||||
.reqopt("", "channel", "current Rust channel", "CHANNEL")
|
||||
.optflag(
|
||||
|
|
@ -206,9 +205,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
|
||||
let target = opt_str2(matches.opt_str("target"));
|
||||
let android_cross_path = opt_path(matches, "android-cross-path");
|
||||
let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target);
|
||||
let (gdb, gdb_version) = analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
|
||||
let lldb_version = matches.opt_str("lldb-version").as_deref().and_then(extract_lldb_version);
|
||||
let (cdb, cdb_version) = debuggers::analyze_cdb(matches.opt_str("cdb"), &target);
|
||||
let (gdb, gdb_version) =
|
||||
debuggers::analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
|
||||
let lldb_version =
|
||||
matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version);
|
||||
let color = match matches.opt_str("color").as_deref() {
|
||||
Some("auto") | None => ColorConfig::AutoColor,
|
||||
Some("always") => ColorConfig::AlwaysColor,
|
||||
|
|
@ -269,8 +270,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
python: matches.opt_str("python").unwrap(),
|
||||
jsondocck_path: matches.opt_str("jsondocck-path"),
|
||||
jsondoclint_path: matches.opt_str("jsondoclint-path"),
|
||||
valgrind_path: matches.opt_str("valgrind-path"),
|
||||
force_valgrind: matches.opt_present("force-valgrind"),
|
||||
run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
|
||||
llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
|
||||
llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
|
||||
|
|
@ -359,7 +358,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
nightly_branch: matches.opt_str("nightly-branch").unwrap(),
|
||||
git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
|
||||
|
||||
profiler_support: matches.opt_present("profiler-support"),
|
||||
profiler_runtime: matches.opt_present("profiler-runtime"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -447,9 +446,9 @@ pub fn run_tests(config: Arc<Config>) {
|
|||
if let Mode::DebugInfo = config.mode {
|
||||
// Debugging emscripten code doesn't make sense today
|
||||
if !config.target.contains("emscripten") {
|
||||
configs.extend(configure_cdb(&config));
|
||||
configs.extend(configure_gdb(&config));
|
||||
configs.extend(configure_lldb(&config));
|
||||
configs.extend(debuggers::configure_cdb(&config));
|
||||
configs.extend(debuggers::configure_gdb(&config));
|
||||
configs.extend(debuggers::configure_lldb(&config));
|
||||
}
|
||||
} else {
|
||||
configs.push(config.clone());
|
||||
|
|
@ -502,62 +501,6 @@ pub fn run_tests(config: Arc<Config>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn configure_cdb(config: &Config) -> Option<Arc<Config>> {
|
||||
config.cdb.as_ref()?;
|
||||
|
||||
Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() }))
|
||||
}
|
||||
|
||||
fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
|
||||
config.gdb_version?;
|
||||
|
||||
if config.matches_env("msvc") {
|
||||
return None;
|
||||
}
|
||||
|
||||
if config.remote_test_client.is_some() && !config.target.contains("android") {
|
||||
println!(
|
||||
"WARNING: debuginfo tests are not available when \
|
||||
testing with remote"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
if config.target.contains("android") {
|
||||
println!(
|
||||
"{} debug-info test uses tcp 5039 port.\
|
||||
please reserve it",
|
||||
config.target
|
||||
);
|
||||
|
||||
// android debug-info test uses remote debugger so, we test 1 thread
|
||||
// at once as they're all sharing the same TCP port to communicate
|
||||
// over.
|
||||
//
|
||||
// we should figure out how to lift this restriction! (run them all
|
||||
// on different ports allocated dynamically).
|
||||
env::set_var("RUST_TEST_THREADS", "1");
|
||||
}
|
||||
|
||||
Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
|
||||
}
|
||||
|
||||
fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
|
||||
config.lldb_python_dir.as_ref()?;
|
||||
|
||||
if let Some(350) = config.lldb_version {
|
||||
println!(
|
||||
"WARNING: The used version of LLDB (350) has a \
|
||||
known issue that breaks debuginfo tests. See \
|
||||
issue #32520 for more information. Skipping all \
|
||||
LLDB-based tests!",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
|
||||
}
|
||||
|
||||
pub fn test_opts(config: &Config) -> test::TestOpts {
|
||||
if env::var("RUST_TEST_NOCAPTURE").is_ok() {
|
||||
eprintln!(
|
||||
|
|
@ -866,7 +809,8 @@ fn files_related_to_test(
|
|||
related.push(testpaths.file.clone());
|
||||
}
|
||||
|
||||
for aux in &props.aux {
|
||||
for aux in props.aux.all_aux_path_strings() {
|
||||
// FIXME(Zalathar): Perform all `auxiliary` path resolution in one place.
|
||||
let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
|
||||
related.push(path);
|
||||
}
|
||||
|
|
@ -984,212 +928,6 @@ fn make_test_closure(
|
|||
}))
|
||||
}
|
||||
|
||||
/// Returns `true` if the given target is an Android target for the
|
||||
/// purposes of GDB testing.
|
||||
fn is_android_gdb_target(target: &str) -> bool {
|
||||
matches!(
|
||||
&target[..],
|
||||
"arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing.
|
||||
fn is_pc_windows_msvc_target(target: &str) -> bool {
|
||||
target.ends_with("-pc-windows-msvc")
|
||||
}
|
||||
|
||||
fn find_cdb(target: &str) -> Option<OsString> {
|
||||
if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
|
||||
let cdb_arch = if cfg!(target_arch = "x86") {
|
||||
"x86"
|
||||
} else if cfg!(target_arch = "x86_64") {
|
||||
"x64"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"arm64"
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
"arm"
|
||||
} else {
|
||||
return None; // No compatible CDB.exe in the Windows 10 SDK
|
||||
};
|
||||
|
||||
let mut path = PathBuf::new();
|
||||
path.push(pf86);
|
||||
path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
|
||||
path.push(cdb_arch);
|
||||
path.push(r"cdb.exe");
|
||||
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(path.into_os_string())
|
||||
}
|
||||
|
||||
/// Returns Path to CDB
|
||||
fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) {
|
||||
let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
|
||||
|
||||
let mut version = None;
|
||||
if let Some(cdb) = cdb.as_ref() {
|
||||
if let Ok(output) = Command::new(cdb).arg("/version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
version = extract_cdb_version(&first_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(cdb, version)
|
||||
}
|
||||
|
||||
fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
|
||||
// Example full_version_line: "cdb version 10.0.18362.1"
|
||||
let version = full_version_line.rsplit(' ').next()?;
|
||||
let mut components = version.split('.');
|
||||
let major: u16 = components.next().unwrap().parse().unwrap();
|
||||
let minor: u16 = components.next().unwrap().parse().unwrap();
|
||||
let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
|
||||
let build: u16 = components.next().unwrap_or("0").parse().unwrap();
|
||||
Some([major, minor, patch, build])
|
||||
}
|
||||
|
||||
/// Returns (Path to GDB, GDB Version)
|
||||
fn analyze_gdb(
|
||||
gdb: Option<String>,
|
||||
target: &str,
|
||||
android_cross_path: &PathBuf,
|
||||
) -> (Option<String>, Option<u32>) {
|
||||
#[cfg(not(windows))]
|
||||
const GDB_FALLBACK: &str = "gdb";
|
||||
#[cfg(windows)]
|
||||
const GDB_FALLBACK: &str = "gdb.exe";
|
||||
|
||||
let fallback_gdb = || {
|
||||
if is_android_gdb_target(target) {
|
||||
let mut gdb_path = match android_cross_path.to_str() {
|
||||
Some(x) => x.to_owned(),
|
||||
None => panic!("cannot find android cross path"),
|
||||
};
|
||||
gdb_path.push_str("/bin/gdb");
|
||||
gdb_path
|
||||
} else {
|
||||
GDB_FALLBACK.to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
let gdb = match gdb {
|
||||
None => fallback_gdb(),
|
||||
Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
|
||||
Some(ref s) => s.to_owned(),
|
||||
};
|
||||
|
||||
let mut version_line = None;
|
||||
if let Ok(output) = Command::new(&gdb).arg("--version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
version_line = Some(first_line.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let version = match version_line {
|
||||
Some(line) => extract_gdb_version(&line),
|
||||
None => return (None, None),
|
||||
};
|
||||
|
||||
(Some(gdb), version)
|
||||
}
|
||||
|
||||
fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
|
||||
let full_version_line = full_version_line.trim();
|
||||
|
||||
// GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
|
||||
// of the ? sections being optional
|
||||
|
||||
// We will parse up to 3 digits for each component, ignoring the date
|
||||
|
||||
// We skip text in parentheses. This avoids accidentally parsing
|
||||
// the openSUSE version, which looks like:
|
||||
// GNU gdb (GDB; openSUSE Leap 15.0) 8.1
|
||||
// This particular form is documented in the GNU coding standards:
|
||||
// https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
|
||||
|
||||
let unbracketed_part = full_version_line.split('[').next().unwrap();
|
||||
let mut splits = unbracketed_part.trim_end().rsplit(' ');
|
||||
let version_string = splits.next().unwrap();
|
||||
|
||||
let mut splits = version_string.split('.');
|
||||
let major = splits.next().unwrap();
|
||||
let minor = splits.next().unwrap();
|
||||
let patch = splits.next();
|
||||
|
||||
let major: u32 = major.parse().unwrap();
|
||||
let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
|
||||
None => {
|
||||
let minor = minor.parse().unwrap();
|
||||
let patch: u32 = match patch {
|
||||
Some(patch) => match patch.find(not_a_digit) {
|
||||
None => patch.parse().unwrap(),
|
||||
Some(idx) if idx > 3 => 0,
|
||||
Some(idx) => patch[..idx].parse().unwrap(),
|
||||
},
|
||||
None => 0,
|
||||
};
|
||||
(minor, patch)
|
||||
}
|
||||
// There is no patch version after minor-date (e.g. "4-2012").
|
||||
Some(idx) => {
|
||||
let minor = minor[..idx].parse().unwrap();
|
||||
(minor, 0)
|
||||
}
|
||||
};
|
||||
|
||||
Some(((major * 1000) + minor) * 1000 + patch)
|
||||
}
|
||||
|
||||
/// Returns LLDB version
|
||||
fn extract_lldb_version(full_version_line: &str) -> Option<u32> {
|
||||
// Extract the major LLDB version from the given version string.
|
||||
// LLDB version strings are different for Apple and non-Apple platforms.
|
||||
// The Apple variant looks like this:
|
||||
//
|
||||
// LLDB-179.5 (older versions)
|
||||
// lldb-300.2.51 (new versions)
|
||||
//
|
||||
// We are only interested in the major version number, so this function
|
||||
// will return `Some(179)` and `Some(300)` respectively.
|
||||
//
|
||||
// Upstream versions look like:
|
||||
// lldb version 6.0.1
|
||||
//
|
||||
// There doesn't seem to be a way to correlate the Apple version
|
||||
// with the upstream version, and since the tests were originally
|
||||
// written against Apple versions, we make a fake Apple version by
|
||||
// multiplying the first number by 100. This is a hack.
|
||||
|
||||
let full_version_line = full_version_line.trim();
|
||||
|
||||
if let Some(apple_ver) =
|
||||
full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
|
||||
{
|
||||
if let Some(idx) = apple_ver.find(not_a_digit) {
|
||||
let version: u32 = apple_ver[..idx].parse().unwrap();
|
||||
return Some(version);
|
||||
}
|
||||
} else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
|
||||
if let Some(idx) = lldb_ver.find(not_a_digit) {
|
||||
let version: u32 = lldb_ver[..idx].parse().ok()?;
|
||||
return Some(version * 100);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn not_a_digit(c: char) -> bool {
|
||||
!c.is_ascii_digit()
|
||||
}
|
||||
|
||||
fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) {
|
||||
let mut collisions = Vec::new();
|
||||
for path in found_paths {
|
||||
|
|
|
|||
|
|
@ -18,15 +18,11 @@ fn main() {
|
|||
|
||||
let config = Arc::new(parse_config(env::args().collect()));
|
||||
|
||||
if config.valgrind_path.is_none() && config.force_valgrind {
|
||||
panic!("Can't find Valgrind to run Valgrind tests");
|
||||
}
|
||||
|
||||
if !config.has_tidy && config.mode == Mode::Rustdoc {
|
||||
eprintln!("warning: `tidy` is not installed; diffs will not be generated");
|
||||
}
|
||||
|
||||
if !config.profiler_support && config.mode == Mode::CoverageRun {
|
||||
if !config.profiler_runtime && config.mode == Mode::CoverageRun {
|
||||
let actioned = if config.bless { "blessed" } else { "checked" };
|
||||
eprintln!(
|
||||
r#"
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ use tracing::*;
|
|||
use crate::common::{
|
||||
Assembly, Codegen, CodegenUnits, CompareMode, Config, CoverageMap, CoverageRun, Crashes,
|
||||
DebugInfo, Debugger, FailMode, Incremental, JsDocTest, MirOpt, PassMode, Pretty, RunMake,
|
||||
RunPassValgrind, Rustdoc, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR,
|
||||
UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path,
|
||||
incremental_dir, output_base_dir, output_base_name, output_testname_unique,
|
||||
Rustdoc, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT,
|
||||
UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir,
|
||||
output_base_dir, output_base_name, output_testname_unique,
|
||||
};
|
||||
use crate::compute_diff::{write_diff, write_filtered_diff};
|
||||
use crate::errors::{self, Error, ErrorKind};
|
||||
|
|
@ -49,7 +49,6 @@ mod run_make;
|
|||
mod rustdoc;
|
||||
mod rustdoc_json;
|
||||
mod ui;
|
||||
mod valgrind;
|
||||
// tidy-alphabet-end
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -253,7 +252,6 @@ impl<'test> TestCx<'test> {
|
|||
self.fatal("cannot use should-ice in a test that is not cfail");
|
||||
}
|
||||
match self.config.mode {
|
||||
RunPassValgrind => self.run_valgrind_test(),
|
||||
Pretty => self.run_pretty_test(),
|
||||
DebugInfo => self.run_debuginfo_test(),
|
||||
Codegen => self.run_codegen_test(),
|
||||
|
|
@ -320,10 +318,29 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_if_test_should_compile(&self, proc_res: &ProcRes, pm: Option<PassMode>) {
|
||||
if self.should_compile_successfully(pm) {
|
||||
fn check_if_test_should_compile(
|
||||
&self,
|
||||
fail_mode: Option<FailMode>,
|
||||
pass_mode: Option<PassMode>,
|
||||
proc_res: &ProcRes,
|
||||
) {
|
||||
if self.should_compile_successfully(pass_mode) {
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
|
||||
match (fail_mode, pass_mode) {
|
||||
(Some(FailMode::Build), Some(PassMode::Check)) => {
|
||||
// A `build-fail` test needs to `check-pass`.
|
||||
self.fatal_proc_rec(
|
||||
"`build-fail` test is required to pass check build, but check build failed",
|
||||
proc_res,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
self.fatal_proc_rec(
|
||||
"test compilation failed although it shouldn't!",
|
||||
proc_res,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if proc_res.status.success() {
|
||||
|
|
@ -843,13 +860,13 @@ impl<'test> TestCx<'test> {
|
|||
/// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
|
||||
fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes {
|
||||
if self.props.build_aux_docs {
|
||||
for rel_ab in &self.props.aux_builds {
|
||||
for rel_ab in &self.props.aux.builds {
|
||||
let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab);
|
||||
let aux_props =
|
||||
let props_for_aux =
|
||||
self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
|
||||
let aux_cx = TestCx {
|
||||
config: self.config,
|
||||
props: &aux_props,
|
||||
props: &props_for_aux,
|
||||
testpaths: &aux_testpaths,
|
||||
revision: self.revision,
|
||||
};
|
||||
|
|
@ -1061,11 +1078,11 @@ impl<'test> TestCx<'test> {
|
|||
fn aux_output_dir(&self) -> PathBuf {
|
||||
let aux_dir = self.aux_output_dir_name();
|
||||
|
||||
if !self.props.aux_builds.is_empty() {
|
||||
if !self.props.aux.builds.is_empty() {
|
||||
remove_and_create_dir_all(&aux_dir);
|
||||
}
|
||||
|
||||
if !self.props.aux_bins.is_empty() {
|
||||
if !self.props.aux.bins.is_empty() {
|
||||
let aux_bin_dir = self.aux_bin_output_dir_name();
|
||||
remove_and_create_dir_all(&aux_dir);
|
||||
remove_and_create_dir_all(&aux_bin_dir);
|
||||
|
|
@ -1075,15 +1092,15 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
|
||||
fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) {
|
||||
for rel_ab in &self.props.aux_builds {
|
||||
for rel_ab in &self.props.aux.builds {
|
||||
self.build_auxiliary(of, rel_ab, &aux_dir, false /* is_bin */);
|
||||
}
|
||||
|
||||
for rel_ab in &self.props.aux_bins {
|
||||
for rel_ab in &self.props.aux.bins {
|
||||
self.build_auxiliary(of, rel_ab, &aux_dir, true /* is_bin */);
|
||||
}
|
||||
|
||||
for (aux_name, aux_path) in &self.props.aux_crates {
|
||||
for (aux_name, aux_path) in &self.props.aux.crates {
|
||||
let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir, false /* is_bin */);
|
||||
let lib_name =
|
||||
get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), aux_type);
|
||||
|
|
@ -1099,7 +1116,7 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
// Build any `//@ aux-codegen-backend`, and pass the resulting library
|
||||
// to `-Zcodegen-backend` when compiling the test file.
|
||||
if let Some(aux_file) = &self.props.aux_codegen_backend {
|
||||
if let Some(aux_file) = &self.props.aux.codegen_backend {
|
||||
let aux_type = self.build_auxiliary(of, aux_file, aux_dir, false);
|
||||
if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) {
|
||||
let lib_path = aux_dir.join(&lib_name);
|
||||
|
|
@ -1500,8 +1517,7 @@ impl<'test> TestCx<'test> {
|
|||
Crashes => {
|
||||
set_mir_dump_dir(&mut rustc);
|
||||
}
|
||||
RunPassValgrind | Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake
|
||||
| CodegenUnits | JsDocTest => {
|
||||
Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | CodegenUnits | JsDocTest => {
|
||||
// do not use JSON output
|
||||
}
|
||||
}
|
||||
|
|
@ -1783,58 +1799,14 @@ impl<'test> TestCx<'test> {
|
|||
proc_res.fatal(None, || on_failure(*self));
|
||||
}
|
||||
|
||||
fn get_output_file(&self, extension: &str) -> TargetLocation {
|
||||
let thin_lto = self.props.compile_flags.iter().any(|s| s.ends_with("lto=thin"));
|
||||
if thin_lto {
|
||||
TargetLocation::ThisDirectory(self.output_base_dir())
|
||||
} else {
|
||||
// This works with both `--emit asm` (as default output name for the assembly)
|
||||
// and `ptx-linker` because the latter can write output at requested location.
|
||||
let output_path = self.output_base_name().with_extension(extension);
|
||||
|
||||
TargetLocation::ThisFile(output_path.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_filecheck_file(&self, extension: &str) -> PathBuf {
|
||||
let thin_lto = self.props.compile_flags.iter().any(|s| s.ends_with("lto=thin"));
|
||||
if thin_lto {
|
||||
let name = self.testpaths.file.file_stem().unwrap().to_str().unwrap();
|
||||
let canonical_name = name.replace('-', "_");
|
||||
let mut output_file = None;
|
||||
for entry in self.output_base_dir().read_dir().unwrap() {
|
||||
if let Ok(entry) = entry {
|
||||
let entry_path = entry.path();
|
||||
let entry_file = entry_path.file_name().unwrap().to_str().unwrap();
|
||||
if entry_file.starts_with(&format!("{}.{}", name, canonical_name))
|
||||
&& entry_file.ends_with(extension)
|
||||
{
|
||||
assert!(
|
||||
output_file.is_none(),
|
||||
"thinlto doesn't support multiple cgu tests"
|
||||
);
|
||||
output_file = Some(entry_file.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(output_file) = output_file {
|
||||
self.output_base_dir().join(output_file)
|
||||
} else {
|
||||
self.output_base_name().with_extension(extension)
|
||||
}
|
||||
} else {
|
||||
self.output_base_name().with_extension(extension)
|
||||
}
|
||||
}
|
||||
|
||||
// codegen tests (using FileCheck)
|
||||
|
||||
fn compile_test_and_save_ir(&self) -> (ProcRes, PathBuf) {
|
||||
let output_file = self.get_output_file("ll");
|
||||
let output_path = self.output_base_name().with_extension("ll");
|
||||
let input_file = &self.testpaths.file;
|
||||
let rustc = self.make_compile_args(
|
||||
input_file,
|
||||
output_file,
|
||||
TargetLocation::ThisFile(output_path.clone()),
|
||||
Emit::LlvmIr,
|
||||
AllowUnused::No,
|
||||
LinkToAux::Yes,
|
||||
|
|
@ -1842,35 +1814,27 @@ impl<'test> TestCx<'test> {
|
|||
);
|
||||
|
||||
let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
|
||||
let output_path = self.get_filecheck_file("ll");
|
||||
(proc_res, output_path)
|
||||
}
|
||||
|
||||
fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
|
||||
let output_file = self.get_output_file("s");
|
||||
// This works with both `--emit asm` (as default output name for the assembly)
|
||||
// and `ptx-linker` because the latter can write output at requested location.
|
||||
let output_path = self.output_base_name().with_extension("s");
|
||||
let input_file = &self.testpaths.file;
|
||||
|
||||
let mut emit = Emit::None;
|
||||
match self.props.assembly_output.as_ref().map(AsRef::as_ref) {
|
||||
Some("emit-asm") => {
|
||||
emit = Emit::Asm;
|
||||
}
|
||||
|
||||
Some("bpf-linker") => {
|
||||
emit = Emit::LinkArgsAsm;
|
||||
}
|
||||
|
||||
Some("ptx-linker") => {
|
||||
// No extra flags needed.
|
||||
}
|
||||
|
||||
Some(header) => self.fatal(&format!("unknown 'assembly-output' header: {header}")),
|
||||
None => self.fatal("missing 'assembly-output' header"),
|
||||
}
|
||||
// Use the `//@ assembly-output:` directive to determine how to emit assembly.
|
||||
let emit = match self.props.assembly_output.as_deref() {
|
||||
Some("emit-asm") => Emit::Asm,
|
||||
Some("bpf-linker") => Emit::LinkArgsAsm,
|
||||
Some("ptx-linker") => Emit::None, // No extra flags needed.
|
||||
Some(other) => self.fatal(&format!("unknown 'assembly-output' directive: {other}")),
|
||||
None => self.fatal("missing 'assembly-output' directive"),
|
||||
};
|
||||
|
||||
let rustc = self.make_compile_args(
|
||||
input_file,
|
||||
output_file,
|
||||
TargetLocation::ThisFile(output_path.clone()),
|
||||
emit,
|
||||
AllowUnused::No,
|
||||
LinkToAux::Yes,
|
||||
|
|
@ -1878,7 +1842,6 @@ impl<'test> TestCx<'test> {
|
|||
);
|
||||
|
||||
let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
|
||||
let output_path = self.get_filecheck_file("s");
|
||||
(proc_res, output_path)
|
||||
}
|
||||
|
||||
|
|
@ -2109,6 +2072,10 @@ impl<'test> TestCx<'test> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// This method is used for `//@ check-test-line-numbers-match`.
|
||||
///
|
||||
/// It checks that doctests line in the displayed doctest "name" matches where they are
|
||||
/// defined in source code.
|
||||
fn check_rustdoc_test_option(&self, res: ProcRes) {
|
||||
let mut other_files = Vec::new();
|
||||
let mut files: HashMap<String, Vec<usize>> = HashMap::new();
|
||||
|
|
@ -2651,33 +2618,6 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(jieyouxu): `run_rpass_test` is hoisted out here and not in incremental because
|
||||
// apparently valgrind test falls back to `run_rpass_test` if valgrind isn't available, which
|
||||
// seems highly questionable to me.
|
||||
fn run_rpass_test(&self) {
|
||||
let emit_metadata = self.should_emit_metadata(self.pass_mode());
|
||||
let should_run = self.run_if_enabled();
|
||||
let proc_res = self.compile_test(should_run, emit_metadata);
|
||||
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("compilation failed!", &proc_res);
|
||||
}
|
||||
|
||||
// FIXME(#41968): Move this check to tidy?
|
||||
if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
|
||||
self.fatal("run-pass tests with expected warnings should be moved to ui/");
|
||||
}
|
||||
|
||||
if let WillExecute::Disabled = should_run {
|
||||
return;
|
||||
}
|
||||
|
||||
let proc_res = self.exec_compiled_test();
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("test run failed!", &proc_res);
|
||||
}
|
||||
}
|
||||
|
||||
fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
|
||||
for e in path.read_dir()? {
|
||||
let entry = e?;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use tracing::debug;
|
|||
use super::debugger::DebuggerCommands;
|
||||
use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute};
|
||||
use crate::common::Config;
|
||||
use crate::debuggers::{extract_gdb_version, is_android_gdb_target};
|
||||
use crate::util::logv;
|
||||
use crate::{extract_gdb_version, is_android_gdb_target};
|
||||
|
||||
impl TestCx<'_> {
|
||||
pub(super) fn run_debuginfo_test(&self) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
use super::{TestCx, WillExecute};
|
||||
use super::{FailMode, TestCx, WillExecute};
|
||||
use crate::errors;
|
||||
|
||||
// FIXME(jieyouxu): `run_rpass_test` got hoisted out of this because apparently valgrind falls back
|
||||
// to `run_rpass_test` if valgrind isn't available, which is questionable, but keeping it for
|
||||
// refactoring changes to preserve current behavior.
|
||||
|
||||
impl TestCx<'_> {
|
||||
pub(super) fn run_incremental_test(&self) {
|
||||
// Basic plan for a test incremental/foo/bar.rs:
|
||||
|
|
@ -73,10 +69,34 @@ impl TestCx<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn run_rpass_test(&self) {
|
||||
let emit_metadata = self.should_emit_metadata(self.pass_mode());
|
||||
let should_run = self.run_if_enabled();
|
||||
let proc_res = self.compile_test(should_run, emit_metadata);
|
||||
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("compilation failed!", &proc_res);
|
||||
}
|
||||
|
||||
// FIXME(#41968): Move this check to tidy?
|
||||
if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
|
||||
self.fatal("run-pass tests with expected warnings should be moved to ui/");
|
||||
}
|
||||
|
||||
if let WillExecute::Disabled = should_run {
|
||||
return;
|
||||
}
|
||||
|
||||
let proc_res = self.exec_compiled_test();
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("test run failed!", &proc_res);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_cfail_test(&self) {
|
||||
let pm = self.pass_mode();
|
||||
let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
|
||||
self.check_if_test_should_compile(&proc_res, pm);
|
||||
self.check_if_test_should_compile(Some(FailMode::Build), pm, &proc_res);
|
||||
self.check_no_compiler_crash(&proc_res, self.props.should_ice);
|
||||
|
||||
let output_to_check = self.get_output(&proc_res);
|
||||
|
|
@ -115,12 +135,6 @@ impl TestCx<'_> {
|
|||
|
||||
let proc_res = self.exec_compiled_test();
|
||||
|
||||
// The value our Makefile configures valgrind to return on failure
|
||||
const VALGRIND_ERR: i32 = 100;
|
||||
if proc_res.status.code() == Some(VALGRIND_ERR) {
|
||||
self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
|
||||
}
|
||||
|
||||
let output_to_check = self.get_output(&proc_res);
|
||||
self.check_correct_failure_status(&proc_res);
|
||||
self.check_all_error_patterns(&output_to_check, &proc_res, pm);
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ impl TestCx<'_> {
|
|||
let pm = Some(PassMode::Check);
|
||||
let proc_res =
|
||||
self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
|
||||
self.check_if_test_should_compile(&proc_res, pm);
|
||||
self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
|
||||
}
|
||||
|
||||
let pm = self.pass_mode();
|
||||
let should_run = self.should_run(pm);
|
||||
let emit_metadata = self.should_emit_metadata(pm);
|
||||
let proc_res = self.compile_test(should_run, emit_metadata);
|
||||
self.check_if_test_should_compile(&proc_res, pm);
|
||||
self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
|
||||
if matches!(proc_res.truncated, Truncated::Yes)
|
||||
&& !self.props.dont_check_compiler_stdout
|
||||
&& !self.props.dont_check_compiler_stderr
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
use super::{Emit, TestCx, WillExecute};
|
||||
|
||||
impl TestCx<'_> {
|
||||
pub(super) fn run_valgrind_test(&self) {
|
||||
assert!(self.revision.is_none(), "revisions not relevant here");
|
||||
|
||||
// FIXME(jieyouxu): does this really make any sense? If a valgrind test isn't testing
|
||||
// valgrind, what is it even testing?
|
||||
if self.config.valgrind_path.is_none() {
|
||||
assert!(!self.config.force_valgrind);
|
||||
return self.run_rpass_test();
|
||||
}
|
||||
|
||||
let should_run = self.run_if_enabled();
|
||||
let mut proc_res = self.compile_test(should_run, Emit::None);
|
||||
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("compilation failed!", &proc_res);
|
||||
}
|
||||
|
||||
if let WillExecute::Disabled = should_run {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_config = self.config.clone();
|
||||
new_config.runner = new_config.valgrind_path.clone();
|
||||
let new_cx = TestCx { config: &new_config, ..*self };
|
||||
proc_res = new_cx.exec_compiled_test();
|
||||
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("test run failed!", &proc_res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
use super::header::extract_llvm_version;
|
||||
use super::*;
|
||||
use std::ffi::OsString;
|
||||
|
||||
use crate::debuggers::{extract_gdb_version, extract_lldb_version};
|
||||
use crate::header::extract_llvm_version;
|
||||
use crate::is_test;
|
||||
|
||||
#[test]
|
||||
fn test_extract_gdb_version() {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ pub(crate) fn dump_covfun_mappings(
|
|||
expression_resolver.push_operands(lhs, rhs);
|
||||
}
|
||||
|
||||
let mut max_counter = None;
|
||||
for i in 0..num_files {
|
||||
let num_mappings = parser.read_uleb128_u32()?;
|
||||
println!("Number of file {i} mappings: {num_mappings}");
|
||||
|
|
@ -63,6 +64,11 @@ pub(crate) fn dump_covfun_mappings(
|
|||
for _ in 0..num_mappings {
|
||||
let (kind, region) = parser.read_mapping_kind_and_region()?;
|
||||
println!("- {kind:?} at {region:?}");
|
||||
kind.for_each_term(|term| {
|
||||
if let CovTerm::Counter(n) = term {
|
||||
max_counter = max_counter.max(Some(n));
|
||||
}
|
||||
});
|
||||
|
||||
match kind {
|
||||
// Also print expression mappings in resolved form.
|
||||
|
|
@ -83,6 +89,16 @@ pub(crate) fn dump_covfun_mappings(
|
|||
}
|
||||
|
||||
parser.ensure_empty()?;
|
||||
|
||||
// Printing the highest counter ID seen in the functions mappings makes
|
||||
// it easier to determine whether a change to coverage instrumentation
|
||||
// has increased or decreased the number of physical counters needed.
|
||||
// (It's possible for the generated code to have more counters that
|
||||
// aren't used by any mappings, but that should hopefully be rare.)
|
||||
println!("Highest counter ID seen: {}", match max_counter {
|
||||
Some(id) => format!("c{id}"),
|
||||
None => "(none)".to_owned(),
|
||||
});
|
||||
println!();
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -271,6 +287,32 @@ enum MappingKind {
|
|||
},
|
||||
}
|
||||
|
||||
impl MappingKind {
|
||||
fn for_each_term(&self, mut callback: impl FnMut(CovTerm)) {
|
||||
match *self {
|
||||
Self::Code(term) => callback(term),
|
||||
Self::Gap(term) => callback(term),
|
||||
Self::Expansion(_id) => {}
|
||||
Self::Skip => {}
|
||||
Self::Branch { r#true, r#false } => {
|
||||
callback(r#true);
|
||||
callback(r#false);
|
||||
}
|
||||
Self::MCDCBranch {
|
||||
r#true,
|
||||
r#false,
|
||||
condition_id: _,
|
||||
true_next_id: _,
|
||||
false_next_id: _,
|
||||
} => {
|
||||
callback(r#true);
|
||||
callback(r#false);
|
||||
}
|
||||
Self::MCDCDecision { bitmap_idx: _, conditions_num: _ } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MappingRegion {
|
||||
/// Offset of this region's start line, relative to the *start line* of
|
||||
/// the *previous mapping* (or 0). Line numbers are 1-based.
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ impl<'a> Validator<'a> {
|
|||
} else if !self.missing_ids.contains(id) {
|
||||
self.missing_ids.insert(id);
|
||||
|
||||
let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone()));
|
||||
let sels = json_find::find_selector(&self.krate_json, &Value::Number(id.0.into()));
|
||||
assert_ne!(sels.len(), 0);
|
||||
|
||||
self.fail(id, ErrorKind::NotFound(sels))
|
||||
|
|
|
|||
|
|
@ -15,24 +15,20 @@ fn check(krate: &Crate, errs: &[Error]) {
|
|||
assert_eq!(errs, &validator.errs[..]);
|
||||
}
|
||||
|
||||
fn id(s: &str) -> Id {
|
||||
Id(s.to_owned())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_on_missing_links() {
|
||||
let k = Crate {
|
||||
root: id("0"),
|
||||
root: Id(0),
|
||||
crate_version: None,
|
||||
includes_private: false,
|
||||
index: FxHashMap::from_iter([(id("0"), Item {
|
||||
index: FxHashMap::from_iter([(Id(0), Item {
|
||||
name: Some("root".to_owned()),
|
||||
id: id(""),
|
||||
id: Id(0),
|
||||
crate_id: 0,
|
||||
span: None,
|
||||
visibility: Visibility::Public,
|
||||
docs: None,
|
||||
links: FxHashMap::from_iter([("Not Found".to_owned(), id("1"))]),
|
||||
links: FxHashMap::from_iter([("Not Found".to_owned(), Id(1))]),
|
||||
attrs: vec![],
|
||||
deprecation: None,
|
||||
inner: ItemEnum::Module(Module { is_crate: true, items: vec![], is_stripped: false }),
|
||||
|
|
@ -49,7 +45,7 @@ fn errors_on_missing_links() {
|
|||
SelectorPart::Field("links".to_owned()),
|
||||
SelectorPart::Field("Not Found".to_owned()),
|
||||
]]),
|
||||
id: id("1"),
|
||||
id: Id(1),
|
||||
}]);
|
||||
}
|
||||
|
||||
|
|
@ -58,28 +54,28 @@ fn errors_on_missing_links() {
|
|||
#[test]
|
||||
fn errors_on_local_in_paths_and_not_index() {
|
||||
let krate = Crate {
|
||||
root: id("0:0:1572"),
|
||||
root: Id(0),
|
||||
crate_version: None,
|
||||
includes_private: false,
|
||||
index: FxHashMap::from_iter([
|
||||
(id("0:0:1572"), Item {
|
||||
id: id("0:0:1572"),
|
||||
(Id(0), Item {
|
||||
id: Id(0),
|
||||
crate_id: 0,
|
||||
name: Some("microcore".to_owned()),
|
||||
span: None,
|
||||
visibility: Visibility::Public,
|
||||
docs: None,
|
||||
links: FxHashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]),
|
||||
links: FxHashMap::from_iter([(("prim@i32".to_owned(), Id(2)))]),
|
||||
attrs: Vec::new(),
|
||||
deprecation: None,
|
||||
inner: ItemEnum::Module(Module {
|
||||
is_crate: true,
|
||||
items: vec![id("0:1:717")],
|
||||
items: vec![Id(1)],
|
||||
is_stripped: false,
|
||||
}),
|
||||
}),
|
||||
(id("0:1:717"), Item {
|
||||
id: id("0:1:717"),
|
||||
(Id(1), Item {
|
||||
id: Id(1),
|
||||
crate_id: 0,
|
||||
name: Some("i32".to_owned()),
|
||||
span: None,
|
||||
|
|
@ -91,7 +87,7 @@ fn errors_on_local_in_paths_and_not_index() {
|
|||
inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }),
|
||||
}),
|
||||
]),
|
||||
paths: FxHashMap::from_iter([(id("0:1:1571"), ItemSummary {
|
||||
paths: FxHashMap::from_iter([(Id(2), ItemSummary {
|
||||
crate_id: 0,
|
||||
path: vec!["microcore".to_owned(), "i32".to_owned()],
|
||||
kind: ItemKind::Primitive,
|
||||
|
|
@ -101,7 +97,7 @@ fn errors_on_local_in_paths_and_not_index() {
|
|||
};
|
||||
|
||||
check(&krate, &[Error {
|
||||
id: id("0:1:1571"),
|
||||
id: Id(2),
|
||||
kind: ErrorKind::Custom("Id for local item in `paths` but not in `index`".to_owned()),
|
||||
}]);
|
||||
}
|
||||
|
|
@ -110,11 +106,11 @@ fn errors_on_local_in_paths_and_not_index() {
|
|||
#[should_panic = "LOCAL_CRATE_ID is wrong"]
|
||||
fn checks_local_crate_id_is_correct() {
|
||||
let krate = Crate {
|
||||
root: id("root"),
|
||||
root: Id(0),
|
||||
crate_version: None,
|
||||
includes_private: false,
|
||||
index: FxHashMap::from_iter([(id("root"), Item {
|
||||
id: id("root"),
|
||||
index: FxHashMap::from_iter([(Id(0), Item {
|
||||
id: Id(0),
|
||||
crate_id: LOCAL_CRATE_ID.wrapping_add(1),
|
||||
name: Some("irrelavent".to_owned()),
|
||||
span: None,
|
||||
|
|
|
|||
|
|
@ -473,14 +473,14 @@ pub fn report_leaks<'tcx>(
|
|||
leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
|
||||
) {
|
||||
let mut any_pruned = false;
|
||||
for (id, kind, mut alloc) in leaks {
|
||||
for (id, kind, alloc) in leaks {
|
||||
let mut title = format!(
|
||||
"memory leaked: {id:?} ({}, size: {:?}, align: {:?})",
|
||||
kind,
|
||||
alloc.size().bytes(),
|
||||
alloc.align.bytes()
|
||||
);
|
||||
let Some(backtrace) = alloc.extra.backtrace.take() else {
|
||||
let Some(backtrace) = alloc.extra.backtrace else {
|
||||
ecx.tcx.dcx().err(title);
|
||||
continue;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ pub fn eval_entry<'tcx>(
|
|||
}
|
||||
// Check for memory leaks.
|
||||
info!("Additional static roots: {:?}", ecx.machine.static_roots);
|
||||
let leaks = ecx.find_leaked_allocations(&ecx.machine.static_roots);
|
||||
let leaks = ecx.take_leaked_allocations(|ecx| &ecx.machine.static_roots);
|
||||
if !leaks.is_empty() {
|
||||
report_leaks(&ecx, leaks);
|
||||
tcx.dcx().note("set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check");
|
||||
|
|
|
|||
|
|
@ -295,6 +295,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"fmuladdf32" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let c = this.read_scalar(c)?.to_f32()?;
|
||||
let fuse: bool = this.machine.rng.get_mut().gen();
|
||||
let res = if fuse {
|
||||
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
|
||||
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
|
||||
} else {
|
||||
((a * b).value + c).value
|
||||
};
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"fmuladdf64" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let c = this.read_scalar(c)?.to_f64()?;
|
||||
let fuse: bool = this.machine.rng.get_mut().gen();
|
||||
let res = if fuse {
|
||||
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
|
||||
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
|
||||
} else {
|
||||
((a * b).value + c).value
|
||||
};
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powf32" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f32()?;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#![feature(rustc_private)]
|
||||
#![feature(cell_update)]
|
||||
#![feature(const_option)]
|
||||
#![feature(float_gamma)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(never_type)]
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ impl ProvenanceExtra {
|
|||
}
|
||||
|
||||
/// Extra per-allocation data
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct AllocExtra<'tcx> {
|
||||
/// Global state of the borrow tracker, if enabled.
|
||||
pub borrow_tracker: Option<borrow_tracker::AllocState>,
|
||||
|
|
@ -338,6 +338,14 @@ pub struct AllocExtra<'tcx> {
|
|||
pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
|
||||
}
|
||||
|
||||
// We need a `Clone` impl because the machine passes `Allocation` through `Cow`...
|
||||
// but that should never end up actually cloning our `AllocExtra`.
|
||||
impl<'tcx> Clone for AllocExtra<'tcx> {
|
||||
fn clone(&self) -> Self {
|
||||
panic!("our allocations should never be cloned");
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitProvenance for AllocExtra<'_> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ fn main() {
|
|||
libm();
|
||||
test_fast();
|
||||
test_algebraic();
|
||||
test_fmuladd();
|
||||
}
|
||||
|
||||
trait Float: Copy + PartialEq + Debug {
|
||||
|
|
@ -1041,3 +1042,20 @@ fn test_algebraic() {
|
|||
test_operations_f32(11., 2.);
|
||||
test_operations_f32(10., 15.);
|
||||
}
|
||||
|
||||
fn test_fmuladd() {
|
||||
use std::intrinsics::{fmuladdf32, fmuladdf64};
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f32(a: f32, b: f32, c: f32) {
|
||||
assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64, c: f64) {
|
||||
assert_approx_eq!(unsafe { fmuladdf64(a, b, c) }, a * b + c);
|
||||
}
|
||||
|
||||
test_operations_f32(0.1, 0.2, 0.3);
|
||||
test_operations_f64(1.1, 1.2, 1.3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
#![feature(core_intrinsics)]
|
||||
use std::intrinsics::{fmuladdf32, fmuladdf64};
|
||||
|
||||
fn main() {
|
||||
let mut saw_zero = false;
|
||||
let mut saw_nonzero = false;
|
||||
for _ in 0..50 {
|
||||
let a = std::hint::black_box(0.1_f64);
|
||||
let b = std::hint::black_box(0.2);
|
||||
let c = std::hint::black_box(-a * b);
|
||||
// It is unspecified whether the following operation is fused or not. The
|
||||
// following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
|
||||
let x = unsafe { fmuladdf64(a, b, c) };
|
||||
if x == 0.0 {
|
||||
saw_zero = true;
|
||||
} else {
|
||||
saw_nonzero = true;
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
saw_zero && saw_nonzero,
|
||||
"`fmuladdf64` failed to be evaluated as both fused and unfused"
|
||||
);
|
||||
|
||||
let mut saw_zero = false;
|
||||
let mut saw_nonzero = false;
|
||||
for _ in 0..50 {
|
||||
let a = std::hint::black_box(0.1_f32);
|
||||
let b = std::hint::black_box(0.2);
|
||||
let c = std::hint::black_box(-a * b);
|
||||
// It is unspecified whether the following operation is fused or not. The
|
||||
// following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
|
||||
let x = unsafe { fmuladdf32(a, b, c) };
|
||||
if x == 0.0 {
|
||||
saw_zero = true;
|
||||
} else {
|
||||
saw_nonzero = true;
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
saw_zero && saw_nonzero,
|
||||
"`fmuladdf32` failed to be evaluated as both fused and unfused"
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
// Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(never_type)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
|
@ -9,6 +11,7 @@ fn main() {
|
|||
invalid_let();
|
||||
dangling_let_type_annotation();
|
||||
invalid_let_type_annotation();
|
||||
never();
|
||||
}
|
||||
|
||||
fn dangling_match() {
|
||||
|
|
@ -34,6 +37,13 @@ fn invalid_match() {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let x: Uninit<!> = Uninit { uninit: () };
|
||||
match x.value {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dangling_let() {
|
||||
|
|
@ -41,6 +51,11 @@ fn dangling_let() {
|
|||
let ptr = ptr::without_provenance::<bool>(0x40);
|
||||
let _ = *ptr;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let ptr = ptr::without_provenance::<!>(0x40);
|
||||
let _ = *ptr;
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_let() {
|
||||
|
|
@ -49,6 +64,12 @@ fn invalid_let() {
|
|||
let ptr = ptr::addr_of!(val).cast::<bool>();
|
||||
let _ = *ptr;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let val = 3u8;
|
||||
let ptr = ptr::addr_of!(val).cast::<!>();
|
||||
let _ = *ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Adding a type annotation used to change how MIR is generated, make sure we cover both cases.
|
||||
|
|
@ -57,6 +78,11 @@ fn dangling_let_type_annotation() {
|
|||
let ptr = ptr::without_provenance::<bool>(0x40);
|
||||
let _: bool = *ptr;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let ptr = ptr::without_provenance::<!>(0x40);
|
||||
let _: ! = *ptr;
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_let_type_annotation() {
|
||||
|
|
@ -65,7 +91,28 @@ fn invalid_let_type_annotation() {
|
|||
let ptr = ptr::addr_of!(val).cast::<bool>();
|
||||
let _: bool = *ptr;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let val = 3u8;
|
||||
let ptr = ptr::addr_of!(val).cast::<!>();
|
||||
let _: ! = *ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy:
|
||||
// https://github.com/rust-lang/rust/issues/117288
|
||||
// Regression test from <https://github.com/rust-lang/rust/issues/117288>.
|
||||
fn never() {
|
||||
unsafe {
|
||||
let x = 3u8;
|
||||
let x: *const ! = &x as *const u8 as *const _;
|
||||
let _: ! = *x;
|
||||
}
|
||||
|
||||
// Without a type annotation, make sure we don't implicitly coerce `!` to `()`
|
||||
// when we do the noop `*x` (as that would require a `!` *value*, creating
|
||||
// which is UB).
|
||||
unsafe {
|
||||
let x = 3u8;
|
||||
let x: *const ! = &x as *const u8 as *const _;
|
||||
let _ = *x;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ llvm-config = "{llvm_config}"
|
|||
"tests/incremental",
|
||||
"tests/mir-opt",
|
||||
"tests/pretty",
|
||||
"tests/run-pass-valgrind",
|
||||
"tests/ui",
|
||||
"tests/crashes",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,28 +1,23 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
use crate::diff;
|
||||
|
||||
#[test]
|
||||
fn test_diff() {
|
||||
let expected = "foo\nbar\nbaz\n";
|
||||
let actual = "foo\nbar\nbaz\n";
|
||||
#[test]
|
||||
fn test_diff() {
|
||||
let expected = "foo\nbar\nbaz\n";
|
||||
let actual = "foo\nbar\nbaz\n";
|
||||
diff().expected_text("EXPECTED_TEXT", expected).actual_text("ACTUAL_TEXT", actual).run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_panic() {
|
||||
let expected = "foo\nbar\nbaz\n";
|
||||
let actual = "foo\nbaz\nbar\n";
|
||||
|
||||
let output = std::panic::catch_unwind(|| {
|
||||
diff().expected_text("EXPECTED_TEXT", expected).actual_text("ACTUAL_TEXT", actual).run();
|
||||
}
|
||||
})
|
||||
.unwrap_err();
|
||||
|
||||
#[test]
|
||||
fn test_should_panic() {
|
||||
let expected = "foo\nbar\nbaz\n";
|
||||
let actual = "foo\nbaz\nbar\n";
|
||||
|
||||
let output = std::panic::catch_unwind(|| {
|
||||
diff()
|
||||
.expected_text("EXPECTED_TEXT", expected)
|
||||
.actual_text("ACTUAL_TEXT", actual)
|
||||
.run();
|
||||
})
|
||||
.unwrap_err();
|
||||
|
||||
let expected_output = "\
|
||||
let expected_output = "\
|
||||
test failed: `EXPECTED_TEXT` is different from `ACTUAL_TEXT`
|
||||
|
||||
--- EXPECTED_TEXT
|
||||
|
|
@ -34,28 +29,27 @@ test failed: `EXPECTED_TEXT` is different from `ACTUAL_TEXT`
|
|||
-baz
|
||||
";
|
||||
|
||||
assert_eq!(output.downcast_ref::<String>().unwrap(), expected_output);
|
||||
}
|
||||
assert_eq!(output.downcast_ref::<String>().unwrap(), expected_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize() {
|
||||
let expected = "
|
||||
#[test]
|
||||
fn test_normalize() {
|
||||
let expected = "
|
||||
running 2 tests
|
||||
..
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
";
|
||||
let actual = "
|
||||
let actual = "
|
||||
running 2 tests
|
||||
..
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
|
||||
";
|
||||
|
||||
diff()
|
||||
.expected_text("EXPECTED_TEXT", expected)
|
||||
.actual_text("ACTUAL_TEXT", actual)
|
||||
.normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME")
|
||||
.run();
|
||||
}
|
||||
diff()
|
||||
.expected_text("EXPECTED_TEXT", expected)
|
||||
.actual_text("ACTUAL_TEXT", actual)
|
||||
.normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME")
|
||||
.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,30 @@ macro_rules! impl_common_helpers {
|
|||
self
|
||||
}
|
||||
|
||||
/// Configuration for the child process’s standard input (stdin) handle.
|
||||
///
|
||||
/// See [`std::process::Command::stdin`].
|
||||
pub fn stdin<T: Into<::std::process::Stdio>>(&mut self, cfg: T) -> &mut Self {
|
||||
self.cmd.stdin(cfg);
|
||||
self
|
||||
}
|
||||
|
||||
/// Configuration for the child process’s standard output (stdout) handle.
|
||||
///
|
||||
/// See [`std::process::Command::stdout`].
|
||||
pub fn stdout<T: Into<::std::process::Stdio>>(&mut self, cfg: T) -> &mut Self {
|
||||
self.cmd.stdout(cfg);
|
||||
self
|
||||
}
|
||||
|
||||
/// Configuration for the child process’s standard error (stderr) handle.
|
||||
///
|
||||
/// See [`std::process::Command::stderr`].
|
||||
pub fn stderr<T: Into<::std::process::Stdio>>(&mut self, cfg: T) -> &mut Self {
|
||||
self.cmd.stderr(cfg);
|
||||
self
|
||||
}
|
||||
|
||||
/// Inspect what the underlying [`Command`] is up to the
|
||||
/// current construction.
|
||||
pub fn inspect<I>(&mut self, inspector: I) -> &mut Self
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ env:
|
|||
RUSTFLAGS: "-D warnings -W unreachable-pub"
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
FETCH_DEPTH: 0 # pull in the tags for the version string
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||
MACOSX_DEPLOYMENT_TARGET: 13.0
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
||||
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
|
||||
|
||||
|
|
@ -43,10 +43,10 @@ jobs:
|
|||
- os: ubuntu-20.04
|
||||
target: arm-unknown-linux-gnueabihf
|
||||
code-target: linux-armhf
|
||||
- os: macos-12
|
||||
- os: macos-13
|
||||
target: x86_64-apple-darwin
|
||||
code-target: darwin-x64
|
||||
- os: macos-12
|
||||
- os: macos-13
|
||||
target: aarch64-apple-darwin
|
||||
code-target: darwin-arm64
|
||||
|
||||
|
|
|
|||
|
|
@ -145,9 +145,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.10"
|
||||
version = "1.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
|
||||
checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg"
|
||||
|
|
@ -1852,6 +1855,12 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.80"
|
||||
rust-version = "1.81"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["rust-analyzer team"]
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ impl CfgOptions {
|
|||
cfg.fold(&|atom| self.enabled.contains(atom))
|
||||
}
|
||||
|
||||
pub fn check_atom(&self, cfg: &CfgAtom) -> bool {
|
||||
self.enabled.contains(cfg)
|
||||
}
|
||||
|
||||
pub fn insert_atom(&mut self, key: Symbol) {
|
||||
self.enabled.insert(CfgAtom::Flag(key));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ macro_rules! f {
|
|||
}
|
||||
|
||||
struct#0:1@58..64#1# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#1#
|
||||
map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..92#1#std#0:1@93..96#1#::#0:1@96..97#1#collections#0:1@98..109#1#::#0:1@109..110#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
|
||||
map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..93#1#std#0:1@93..96#1#::#0:1@96..98#1#collections#0:1@98..109#1#::#0:1@109..111#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
|
||||
}#0:1@132..133#1#
|
||||
"#]],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
use std::{cmp::Ordering, iter, mem, ops::Not};
|
||||
|
||||
use base_db::{CrateId, CrateOrigin, Dependency, LangCrateOrigin};
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use cfg::{CfgAtom, CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
attrs::{Attr, AttrId},
|
||||
|
|
@ -1324,13 +1324,21 @@ impl DefCollector<'_> {
|
|||
};
|
||||
|
||||
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||
// due to duplicating functions into macro expansions
|
||||
// due to duplicating functions into macro expansions, but only if `cfg(test)` is active,
|
||||
// otherwise they are expanded to nothing and this can impact e.g. diagnostics (due to things
|
||||
// being cfg'ed out).
|
||||
// Ideally we will just expand them to nothing here. But we are only collecting macro calls,
|
||||
// not expanding them, so we have no way to do that.
|
||||
if matches!(
|
||||
def.kind,
|
||||
MacroDefKind::BuiltInAttr(_, expander)
|
||||
if expander.is_test() || expander.is_bench()
|
||||
) {
|
||||
return recollect_without(self);
|
||||
let test_is_active =
|
||||
self.cfg_options.check_atom(&CfgAtom::Flag(sym::test.clone()));
|
||||
if test_is_active {
|
||||
return recollect_without(self);
|
||||
}
|
||||
}
|
||||
|
||||
let call_id = || {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use span::{MacroCallId, Span};
|
|||
|
||||
use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind};
|
||||
|
||||
use super::quote;
|
||||
|
||||
macro_rules! register_builtin {
|
||||
($(($name:ident, $variant:ident) => $expand:ident),* ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -52,15 +54,15 @@ impl BuiltinAttrExpander {
|
|||
}
|
||||
|
||||
register_builtin! {
|
||||
(bench, Bench) => dummy_attr_expand,
|
||||
(bench, Bench) => dummy_gate_test_expand,
|
||||
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
||||
(cfg_eval, CfgEval) => dummy_attr_expand,
|
||||
(derive, Derive) => derive_expand,
|
||||
// derive const is equivalent to derive for our proposes.
|
||||
(derive_const, DeriveConst) => derive_expand,
|
||||
(global_allocator, GlobalAllocator) => dummy_attr_expand,
|
||||
(test, Test) => dummy_attr_expand,
|
||||
(test_case, TestCase) => dummy_attr_expand
|
||||
(test, Test) => dummy_gate_test_expand,
|
||||
(test_case, TestCase) => dummy_gate_test_expand
|
||||
}
|
||||
|
||||
pub fn find_builtin_attr(ident: &name::Name) -> Option<BuiltinAttrExpander> {
|
||||
|
|
@ -76,6 +78,19 @@ fn dummy_attr_expand(
|
|||
ExpandResult::ok(tt.clone())
|
||||
}
|
||||
|
||||
fn dummy_gate_test_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let result = quote::quote! { span=>
|
||||
#[cfg(test)]
|
||||
#tt
|
||||
};
|
||||
ExpandResult::ok(result)
|
||||
}
|
||||
|
||||
/// We generate a very specific expansion here, as we do not actually expand the `#[derive]` attribute
|
||||
/// itself in name res, but we do want to expand it to something for the IDE layer, so that the input
|
||||
/// derive attributes can be downmapped, and resolved as proper paths.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ use crate::{
|
|||
cfg_process,
|
||||
declarative::DeclarativeMacroExpander,
|
||||
fixup::{self, SyntaxFixupUndoInfo},
|
||||
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
|
||||
hygiene::{
|
||||
span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
|
||||
SyntaxContextExt as _,
|
||||
},
|
||||
proc_macro::ProcMacros,
|
||||
span_map::{RealSpanMap, SpanMap, SpanMapRef},
|
||||
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
|
||||
|
|
@ -300,14 +303,16 @@ pub fn expand_speculative(
|
|||
token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
|
||||
|
||||
let syntax_node = node.syntax_node();
|
||||
let token = rev_tmap
|
||||
let (token, _) = rev_tmap
|
||||
.ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
|
||||
.filter_map(|range| syntax_node.covering_element(range).into_token())
|
||||
.min_by_key(|t| {
|
||||
// prefer tokens of the same kind and text
|
||||
.filter_map(|(range, ctx)| syntax_node.covering_element(range).into_token().zip(Some(ctx)))
|
||||
.min_by_key(|(t, ctx)| {
|
||||
// prefer tokens of the same kind and text, as well as non opaque marked ones
|
||||
// Note the inversion of the score here, as we want to prefer the first token in case
|
||||
// of all tokens having the same score
|
||||
(t.kind() != token_to_map.kind()) as u8 + 2 * ((t.text() != token_to_map.text()) as u8)
|
||||
ctx.is_opaque(db) as u8
|
||||
+ 2 * (t.kind() != token_to_map.kind()) as u8
|
||||
+ 4 * ((t.text() != token_to_map.text()) as u8)
|
||||
})?;
|
||||
Some((node.syntax_node(), token))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ pub trait SyntaxContextExt {
|
|||
fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
|
||||
fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
|
||||
fn marks(self, db: &dyn ExpandDatabase) -> Vec<(MacroCallId, Transparency)>;
|
||||
fn is_opaque(self, db: &dyn ExpandDatabase) -> bool;
|
||||
}
|
||||
|
||||
impl SyntaxContextExt for SyntaxContextId {
|
||||
|
|
@ -177,6 +178,9 @@ impl SyntaxContextExt for SyntaxContextId {
|
|||
marks.reverse();
|
||||
marks
|
||||
}
|
||||
fn is_opaque(self, db: &dyn ExpandDatabase) -> bool {
|
||||
!self.is_root() && db.lookup_intern_syntax_context(self).outer_transparency.is_opaque()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Make this a SyntaxContextExt method once we have RPIT
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ mod prettify_macro_expansion_;
|
|||
|
||||
use attrs::collect_attrs;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stdx::TupleExt;
|
||||
use triomphe::Arc;
|
||||
|
||||
use std::hash::Hash;
|
||||
|
|
@ -772,14 +773,15 @@ impl ExpansionInfo {
|
|||
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
|
||||
///
|
||||
/// Note this does a linear search through the entire backing vector of the spanmap.
|
||||
// FIXME: Consider adding a reverse map to ExpansionInfo to get rid of the linear search which
|
||||
// potentially results in quadratic look ups (notably this might improve semantic highlighting perf)
|
||||
pub fn map_range_down_exact(
|
||||
&self,
|
||||
span: Span,
|
||||
) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
|
||||
let tokens = self
|
||||
.exp_map
|
||||
.ranges_with_span_exact(span)
|
||||
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
||||
) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContextId)> + '_>> {
|
||||
let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
|
||||
self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
|
||||
});
|
||||
|
||||
Some(InMacroFile::new(self.expanded.file_id, tokens))
|
||||
}
|
||||
|
|
@ -791,11 +793,10 @@ impl ExpansionInfo {
|
|||
pub fn map_range_down(
|
||||
&self,
|
||||
span: Span,
|
||||
) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
|
||||
let tokens = self
|
||||
.exp_map
|
||||
.ranges_with_span(span)
|
||||
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
||||
) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContextId)> + '_>> {
|
||||
let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
|
||||
self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
|
||||
});
|
||||
|
||||
Some(InMacroFile::new(self.expanded.file_id, tokens))
|
||||
}
|
||||
|
|
@ -845,7 +846,8 @@ impl ExpansionInfo {
|
|||
self.arg.file_id,
|
||||
arg_map
|
||||
.ranges_with_span_exact(span)
|
||||
.filter(|range| range.intersect(arg_range).is_some())
|
||||
.filter(|(range, _)| range.intersect(arg_range).is_some())
|
||||
.map(TupleExt::head)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -382,8 +382,9 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
|||
}
|
||||
|
||||
fn is_object_safe(&self, trait_id: chalk_ir::TraitId<Interner>) -> bool {
|
||||
// FIXME: When cargo is updated, change to dyn_compatibility
|
||||
let trait_ = from_chalk_trait_id(trait_id);
|
||||
crate::object_safety::object_safety(self.db, trait_).is_none()
|
||||
crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none()
|
||||
}
|
||||
|
||||
fn closure_kind(
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ use triomphe::Arc;
|
|||
use crate::{
|
||||
chalk_db,
|
||||
consteval::ConstEvalError,
|
||||
dyn_compatibility::DynCompatibilityViolation,
|
||||
layout::{Layout, LayoutError},
|
||||
lower::{GenericDefaults, GenericPredicates},
|
||||
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
|
||||
mir::{BorrowckResult, MirBody, MirLowerError},
|
||||
object_safety::ObjectSafetyViolation,
|
||||
Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
|
||||
PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
|
||||
};
|
||||
|
|
@ -108,8 +108,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
#[salsa::invoke(crate::layout::target_data_layout_query)]
|
||||
fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>;
|
||||
|
||||
#[salsa::invoke(crate::object_safety::object_safety_of_trait_query)]
|
||||
fn object_safety_of_trait(&self, trait_: TraitId) -> Option<ObjectSafetyViolation>;
|
||||
#[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
|
||||
fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
|
||||
|
||||
#[salsa::invoke(crate::lower::ty_query)]
|
||||
#[salsa::cycle(crate::lower::ty_recover)]
|
||||
|
|
@ -280,8 +280,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn hir_database_is_object_safe() {
|
||||
fn _assert_object_safe(_: &dyn HirDatabase) {}
|
||||
fn hir_database_is_dyn_compatible() {
|
||||
fn _assert_dyn_compatible(_: &dyn HirDatabase) {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ impl fmt::Display for CaseType {
|
|||
let repr = match self {
|
||||
CaseType::LowerSnakeCase => "snake_case",
|
||||
CaseType::UpperSnakeCase => "UPPER_SNAKE_CASE",
|
||||
CaseType::UpperCamelCase => "CamelCase",
|
||||
CaseType::UpperCamelCase => "UpperCamelCase",
|
||||
};
|
||||
|
||||
repr.fmt(f)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ mod tests {
|
|||
check(to_lower_snake_case, "lower_snake_case", expect![[""]]);
|
||||
check(to_lower_snake_case, "UPPER_SNAKE_CASE", expect![["upper_snake_case"]]);
|
||||
check(to_lower_snake_case, "Weird_Case", expect![["weird_case"]]);
|
||||
check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]);
|
||||
check(to_lower_snake_case, "UpperCamelCase", expect![["upper_camel_case"]]);
|
||||
check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]);
|
||||
check(to_lower_snake_case, "a", expect![[""]]);
|
||||
check(to_lower_snake_case, "abc", expect![[""]]);
|
||||
|
|
@ -121,8 +121,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_to_camel_case() {
|
||||
check(to_camel_case, "CamelCase", expect![[""]]);
|
||||
check(to_camel_case, "CamelCase_", expect![[""]]);
|
||||
check(to_camel_case, "UpperCamelCase", expect![[""]]);
|
||||
check(to_camel_case, "UpperCamelCase_", expect![[""]]);
|
||||
check(to_camel_case, "_CamelCase", expect![[""]]);
|
||||
check(to_camel_case, "lowerCamelCase", expect![["LowerCamelCase"]]);
|
||||
check(to_camel_case, "lower_snake_case", expect![["LowerSnakeCase"]]);
|
||||
|
|
@ -143,7 +143,7 @@ mod tests {
|
|||
check(to_upper_snake_case, "UPPER_SNAKE_CASE", expect![[""]]);
|
||||
check(to_upper_snake_case, "lower_snake_case", expect![["LOWER_SNAKE_CASE"]]);
|
||||
check(to_upper_snake_case, "Weird_Case", expect![["WEIRD_CASE"]]);
|
||||
check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]);
|
||||
check(to_upper_snake_case, "UpperCamelCase", expect![["UPPER_CAMEL_CASE"]]);
|
||||
check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]);
|
||||
check(to_upper_snake_case, "A", expect![[""]]);
|
||||
check(to_upper_snake_case, "ABC", expect![[""]]);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! Compute the object-safety of a trait
|
||||
//! Compute the dyn-compatibility of a trait
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
|
|
@ -28,14 +28,14 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ObjectSafetyViolation {
|
||||
pub enum DynCompatibilityViolation {
|
||||
SizedSelf,
|
||||
SelfReferential,
|
||||
Method(FunctionId, MethodViolationCode),
|
||||
AssocConst(ConstId),
|
||||
GAT(TypeAliasId),
|
||||
// This doesn't exist in rustc, but added for better visualization
|
||||
HasNonSafeSuperTrait(TraitId),
|
||||
HasNonCompatibleSuperTrait(TraitId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -50,70 +50,73 @@ pub enum MethodViolationCode {
|
|||
UndispatchableReceiver,
|
||||
}
|
||||
|
||||
pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option<ObjectSafetyViolation> {
|
||||
pub fn dyn_compatibility(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
) -> Option<DynCompatibilityViolation> {
|
||||
for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() {
|
||||
if db.object_safety_of_trait(super_trait).is_some() {
|
||||
return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait));
|
||||
if db.dyn_compatibility_of_trait(super_trait).is_some() {
|
||||
return Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait));
|
||||
}
|
||||
}
|
||||
|
||||
db.object_safety_of_trait(trait_)
|
||||
db.dyn_compatibility_of_trait(trait_)
|
||||
}
|
||||
|
||||
pub fn object_safety_with_callback<F>(
|
||||
pub fn dyn_compatibility_with_callback<F>(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
cb: &mut F,
|
||||
) -> ControlFlow<()>
|
||||
where
|
||||
F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
|
||||
F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>,
|
||||
{
|
||||
for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() {
|
||||
if db.object_safety_of_trait(super_trait).is_some() {
|
||||
cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?;
|
||||
if db.dyn_compatibility_of_trait(super_trait).is_some() {
|
||||
cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?;
|
||||
}
|
||||
}
|
||||
|
||||
object_safety_of_trait_with_callback(db, trait_, cb)
|
||||
dyn_compatibility_of_trait_with_callback(db, trait_, cb)
|
||||
}
|
||||
|
||||
pub fn object_safety_of_trait_with_callback<F>(
|
||||
pub fn dyn_compatibility_of_trait_with_callback<F>(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
cb: &mut F,
|
||||
) -> ControlFlow<()>
|
||||
where
|
||||
F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
|
||||
F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>,
|
||||
{
|
||||
// Check whether this has a `Sized` bound
|
||||
if generics_require_sized_self(db, trait_.into()) {
|
||||
cb(ObjectSafetyViolation::SizedSelf)?;
|
||||
cb(DynCompatibilityViolation::SizedSelf)?;
|
||||
}
|
||||
|
||||
// Check if there exist bounds that referencing self
|
||||
if predicates_reference_self(db, trait_) {
|
||||
cb(ObjectSafetyViolation::SelfReferential)?;
|
||||
cb(DynCompatibilityViolation::SelfReferential)?;
|
||||
}
|
||||
if bounds_reference_self(db, trait_) {
|
||||
cb(ObjectSafetyViolation::SelfReferential)?;
|
||||
cb(DynCompatibilityViolation::SelfReferential)?;
|
||||
}
|
||||
|
||||
// rustc checks for non-lifetime binders here, but we don't support HRTB yet
|
||||
|
||||
let trait_data = db.trait_data(trait_);
|
||||
for (_, assoc_item) in &trait_data.items {
|
||||
object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?;
|
||||
dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?;
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
pub fn object_safety_of_trait_query(
|
||||
pub fn dyn_compatibility_of_trait_query(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
) -> Option<ObjectSafetyViolation> {
|
||||
) -> Option<DynCompatibilityViolation> {
|
||||
let mut res = None;
|
||||
object_safety_of_trait_with_callback(db, trait_, &mut |osv| {
|
||||
dyn_compatibility_of_trait_with_callback(db, trait_, &mut |osv| {
|
||||
res = Some(osv);
|
||||
ControlFlow::Break(())
|
||||
});
|
||||
|
|
@ -321,14 +324,14 @@ fn contains_illegal_self_type_reference<T: TypeVisitable<Interner>>(
|
|||
t.visit_with(visitor.as_dyn(), outer_binder).is_break()
|
||||
}
|
||||
|
||||
fn object_safety_violation_for_assoc_item<F>(
|
||||
fn dyn_compatibility_violation_for_assoc_item<F>(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
item: AssocItemId,
|
||||
cb: &mut F,
|
||||
) -> ControlFlow<()>
|
||||
where
|
||||
F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
|
||||
F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>,
|
||||
{
|
||||
// Any item that has a `Self : Sized` requisite is otherwise
|
||||
// exempt from the regulations.
|
||||
|
|
@ -337,10 +340,10 @@ where
|
|||
}
|
||||
|
||||
match item {
|
||||
AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)),
|
||||
AssocItemId::ConstId(it) => cb(DynCompatibilityViolation::AssocConst(it)),
|
||||
AssocItemId::FunctionId(it) => {
|
||||
virtual_call_violations_for_method(db, trait_, it, &mut |mvc| {
|
||||
cb(ObjectSafetyViolation::Method(it, mvc))
|
||||
cb(DynCompatibilityViolation::Method(it, mvc))
|
||||
})
|
||||
}
|
||||
AssocItemId::TypeAliasId(it) => {
|
||||
|
|
@ -350,7 +353,7 @@ where
|
|||
} else {
|
||||
let generic_params = db.generic_params(item.into());
|
||||
if !generic_params.is_empty() {
|
||||
cb(ObjectSafetyViolation::GAT(it))
|
||||
cb(DynCompatibilityViolation::GAT(it))
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
|
@ -469,7 +472,7 @@ fn receiver_is_dispatchable(
|
|||
return false;
|
||||
};
|
||||
|
||||
// `self: Self` can't be dispatched on, but this is already considered object safe.
|
||||
// `self: Self` can't be dispatched on, but this is already considered dyn compatible
|
||||
// See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437
|
||||
if sig
|
||||
.skip_binders()
|
||||
|
|
@ -5,29 +5,29 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||
use syntax::ToSmolStr;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{object_safety::object_safety_with_callback, test_db::TestDB};
|
||||
use crate::{dyn_compatibility::dyn_compatibility_with_callback, test_db::TestDB};
|
||||
|
||||
use super::{
|
||||
DynCompatibilityViolation,
|
||||
MethodViolationCode::{self, *},
|
||||
ObjectSafetyViolation,
|
||||
};
|
||||
|
||||
use ObjectSafetyViolationKind::*;
|
||||
use DynCompatibilityViolationKind::*;
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum ObjectSafetyViolationKind {
|
||||
enum DynCompatibilityViolationKind {
|
||||
SizedSelf,
|
||||
SelfReferential,
|
||||
Method(MethodViolationCode),
|
||||
AssocConst,
|
||||
GAT,
|
||||
HasNonSafeSuperTrait,
|
||||
HasNonCompatibleSuperTrait,
|
||||
}
|
||||
|
||||
fn check_object_safety<'a>(
|
||||
fn check_dyn_compatibility<'a>(
|
||||
ra_fixture: &str,
|
||||
expected: impl IntoIterator<Item = (&'a str, Vec<ObjectSafetyViolationKind>)>,
|
||||
expected: impl IntoIterator<Item = (&'a str, Vec<DynCompatibilityViolationKind>)>,
|
||||
) {
|
||||
let mut expected: FxHashMap<_, _> =
|
||||
expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect();
|
||||
|
|
@ -53,18 +53,20 @@ fn check_object_safety<'a>(
|
|||
continue;
|
||||
};
|
||||
let mut osvs = FxHashSet::default();
|
||||
object_safety_with_callback(&db, trait_id, &mut |osv| {
|
||||
dyn_compatibility_with_callback(&db, trait_id, &mut |osv| {
|
||||
osvs.insert(match osv {
|
||||
ObjectSafetyViolation::SizedSelf => SizedSelf,
|
||||
ObjectSafetyViolation::SelfReferential => SelfReferential,
|
||||
ObjectSafetyViolation::Method(_, mvc) => Method(mvc),
|
||||
ObjectSafetyViolation::AssocConst(_) => AssocConst,
|
||||
ObjectSafetyViolation::GAT(_) => GAT,
|
||||
ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait,
|
||||
DynCompatibilityViolation::SizedSelf => SizedSelf,
|
||||
DynCompatibilityViolation::SelfReferential => SelfReferential,
|
||||
DynCompatibilityViolation::Method(_, mvc) => Method(mvc),
|
||||
DynCompatibilityViolation::AssocConst(_) => AssocConst,
|
||||
DynCompatibilityViolation::GAT(_) => GAT,
|
||||
DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => {
|
||||
HasNonCompatibleSuperTrait
|
||||
}
|
||||
});
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;");
|
||||
assert_eq!(osvs, expected, "Dyn Compatibility violations for `{name}` do not match;");
|
||||
}
|
||||
|
||||
let remains: Vec<_> = expected.keys().collect();
|
||||
|
|
@ -73,7 +75,7 @@ fn check_object_safety<'a>(
|
|||
|
||||
#[test]
|
||||
fn item_bounds_can_reference_self() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: eq
|
||||
pub trait Foo {
|
||||
|
|
@ -88,7 +90,7 @@ pub trait Foo {
|
|||
|
||||
#[test]
|
||||
fn associated_consts() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
trait Bar {
|
||||
const X: usize;
|
||||
|
|
@ -100,7 +102,7 @@ trait Bar {
|
|||
|
||||
#[test]
|
||||
fn bounds_reference_self() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: eq
|
||||
trait X {
|
||||
|
|
@ -113,7 +115,7 @@ trait X {
|
|||
|
||||
#[test]
|
||||
fn by_value_self() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Bar {
|
||||
|
|
@ -135,7 +137,7 @@ trait Quux {
|
|||
|
||||
#[test]
|
||||
fn generic_methods() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Bar {
|
||||
|
|
@ -157,7 +159,7 @@ trait Qax {
|
|||
|
||||
#[test]
|
||||
fn mentions_self() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Bar {
|
||||
|
|
@ -182,7 +184,7 @@ trait Quux {
|
|||
|
||||
#[test]
|
||||
fn no_static() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Foo {
|
||||
|
|
@ -195,7 +197,7 @@ trait Foo {
|
|||
|
||||
#[test]
|
||||
fn sized_self() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Bar: Sized {
|
||||
|
|
@ -205,7 +207,7 @@ trait Bar: Sized {
|
|||
[("Bar", vec![SizedSelf])],
|
||||
);
|
||||
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Bar
|
||||
|
|
@ -220,7 +222,7 @@ trait Bar
|
|||
|
||||
#[test]
|
||||
fn supertrait_gat() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait GatTrait {
|
||||
|
|
@ -229,13 +231,13 @@ trait GatTrait {
|
|||
|
||||
trait SuperTrait<T>: GatTrait {}
|
||||
"#,
|
||||
[("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])],
|
||||
[("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonCompatibleSuperTrait])],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supertrait_mentions_self() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Bar<T> {
|
||||
|
|
@ -251,7 +253,7 @@ trait Baz : Bar<Self> {
|
|||
|
||||
#[test]
|
||||
fn rustc_issue_19538() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Foo {
|
||||
|
|
@ -260,13 +262,13 @@ trait Foo {
|
|||
|
||||
trait Bar: Foo {}
|
||||
"#,
|
||||
[("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])],
|
||||
[("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonCompatibleSuperTrait])],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustc_issue_22040() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: fmt, eq, dispatch_from_dyn
|
||||
use core::fmt::Debug;
|
||||
|
|
@ -281,7 +283,7 @@ trait Expr: Debug + PartialEq {
|
|||
|
||||
#[test]
|
||||
fn rustc_issue_102762() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: future, send, sync, dispatch_from_dyn, deref
|
||||
use core::pin::Pin;
|
||||
|
|
@ -313,7 +315,7 @@ pub trait Fetcher: Send + Sync {
|
|||
|
||||
#[test]
|
||||
fn rustc_issue_102933() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: future, dispatch_from_dyn, deref
|
||||
use core::future::Future;
|
||||
|
|
@ -351,7 +353,7 @@ pub trait B2: Service<Response = i32> + B1 {
|
|||
|
||||
#[test]
|
||||
fn rustc_issue_106247() {
|
||||
check_object_safety(
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: sync, dispatch_from_dyn
|
||||
pub trait Trait {
|
||||
|
|
@ -363,8 +365,8 @@ pub trait Trait {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn std_error_is_object_safe() {
|
||||
check_object_safety(
|
||||
fn std_error_is_dyn_compatible() {
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: fmt, dispatch_from_dyn
|
||||
trait Erased<'a>: 'a {}
|
||||
|
|
@ -380,14 +382,14 @@ pub trait Error: core::fmt::Debug + core::fmt::Display {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn lifetime_gat_is_object_unsafe() {
|
||||
check_object_safety(
|
||||
fn lifetime_gat_is_dyn_incompatible() {
|
||||
check_dyn_compatibility(
|
||||
r#"
|
||||
//- minicore: dispatch_from_dyn
|
||||
trait Foo {
|
||||
type Bar<'a>;
|
||||
}
|
||||
"#,
|
||||
[("Foo", vec![ObjectSafetyViolationKind::GAT])],
|
||||
[("Foo", vec![DynCompatibilityViolationKind::GAT])],
|
||||
);
|
||||
}
|
||||
|
|
@ -38,11 +38,11 @@ pub mod consteval;
|
|||
pub mod db;
|
||||
pub mod diagnostics;
|
||||
pub mod display;
|
||||
pub mod dyn_compatibility;
|
||||
pub mod lang_items;
|
||||
pub mod layout;
|
||||
pub mod method_resolution;
|
||||
pub mod mir;
|
||||
pub mod object_safety;
|
||||
pub mod primitive;
|
||||
pub mod traits;
|
||||
|
||||
|
|
|
|||
|
|
@ -386,82 +386,91 @@ fn ever_initialized_map(
|
|||
fn dfs(
|
||||
db: &dyn HirDatabase,
|
||||
body: &MirBody,
|
||||
b: BasicBlockId,
|
||||
l: LocalId,
|
||||
stack: &mut Vec<BasicBlockId>,
|
||||
result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
|
||||
) {
|
||||
let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
|
||||
let block = &body.basic_blocks[b];
|
||||
for statement in &block.statements {
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(p, _) => {
|
||||
if p.projection.lookup(&body.projection_store).is_empty() && p.local == l {
|
||||
while let Some(b) = stack.pop() {
|
||||
let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
|
||||
let block = &body.basic_blocks[b];
|
||||
for statement in &block.statements {
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(p, _) => {
|
||||
if p.projection.lookup(&body.projection_store).is_empty() && p.local == l {
|
||||
is_ever_initialized = true;
|
||||
}
|
||||
}
|
||||
StatementKind::StorageDead(p) => {
|
||||
if *p == l {
|
||||
is_ever_initialized = false;
|
||||
}
|
||||
}
|
||||
StatementKind::Deinit(_)
|
||||
| StatementKind::FakeRead(_)
|
||||
| StatementKind::Nop
|
||||
| StatementKind::StorageLive(_) => (),
|
||||
}
|
||||
}
|
||||
let Some(terminator) = &block.terminator else {
|
||||
never!(
|
||||
"Terminator should be none only in construction.\nThe body:\n{}",
|
||||
body.pretty_print(db)
|
||||
);
|
||||
return;
|
||||
};
|
||||
let mut process = |target, is_ever_initialized| {
|
||||
if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
|
||||
result[target].insert(l, is_ever_initialized);
|
||||
stack.push(target);
|
||||
}
|
||||
};
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
|
||||
TerminatorKind::SwitchInt { targets, .. } => {
|
||||
targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
|
||||
}
|
||||
TerminatorKind::UnwindResume
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable => (),
|
||||
TerminatorKind::Call { target, cleanup, destination, .. } => {
|
||||
if destination.projection.lookup(&body.projection_store).is_empty()
|
||||
&& destination.local == l
|
||||
{
|
||||
is_ever_initialized = true;
|
||||
}
|
||||
target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
|
||||
}
|
||||
StatementKind::StorageDead(p) => {
|
||||
if *p == l {
|
||||
is_ever_initialized = false;
|
||||
}
|
||||
TerminatorKind::Drop { target, unwind, place: _ } => {
|
||||
iter::once(target)
|
||||
.chain(unwind)
|
||||
.for_each(|&it| process(it, is_ever_initialized));
|
||||
}
|
||||
StatementKind::Deinit(_)
|
||||
| StatementKind::FakeRead(_)
|
||||
| StatementKind::Nop
|
||||
| StatementKind::StorageLive(_) => (),
|
||||
}
|
||||
}
|
||||
let Some(terminator) = &block.terminator else {
|
||||
never!(
|
||||
"Terminator should be none only in construction.\nThe body:\n{}",
|
||||
body.pretty_print(db)
|
||||
);
|
||||
return;
|
||||
};
|
||||
let mut process = |target, is_ever_initialized| {
|
||||
if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
|
||||
result[target].insert(l, is_ever_initialized);
|
||||
dfs(db, body, target, l, result);
|
||||
}
|
||||
};
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
|
||||
TerminatorKind::SwitchInt { targets, .. } => {
|
||||
targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
|
||||
}
|
||||
TerminatorKind::UnwindResume
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable => (),
|
||||
TerminatorKind::Call { target, cleanup, destination, .. } => {
|
||||
if destination.projection.lookup(&body.projection_store).is_empty()
|
||||
&& destination.local == l
|
||||
{
|
||||
is_ever_initialized = true;
|
||||
TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::Yield { .. }
|
||||
| TerminatorKind::CoroutineDrop
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. } => {
|
||||
never!("We don't emit these MIR terminators yet");
|
||||
}
|
||||
target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
|
||||
}
|
||||
TerminatorKind::Drop { target, unwind, place: _ } => {
|
||||
iter::once(target).chain(unwind).for_each(|&it| process(it, is_ever_initialized));
|
||||
}
|
||||
TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::Yield { .. }
|
||||
| TerminatorKind::CoroutineDrop
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. } => {
|
||||
never!("We don't emit these MIR terminators yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut stack = Vec::new();
|
||||
for &l in &body.param_locals {
|
||||
result[body.start_block].insert(l, true);
|
||||
dfs(db, body, body.start_block, l, &mut result);
|
||||
stack.clear();
|
||||
stack.push(body.start_block);
|
||||
dfs(db, body, l, &mut stack, &mut result);
|
||||
}
|
||||
for l in body.locals.iter().map(|it| it.0) {
|
||||
db.unwind_if_cancelled();
|
||||
if !result[body.start_block].contains_idx(l) {
|
||||
result[body.start_block].insert(l, false);
|
||||
dfs(db, body, body.start_block, l, &mut result);
|
||||
stack.clear();
|
||||
stack.push(body.start_block);
|
||||
dfs(db, body, l, &mut stack, &mut result);
|
||||
}
|
||||
}
|
||||
result
|
||||
|
|
|
|||
|
|
@ -144,9 +144,9 @@ pub use {
|
|||
hir_ty::{
|
||||
consteval::ConstEvalError,
|
||||
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
|
||||
dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode},
|
||||
layout::LayoutError,
|
||||
mir::{MirEvalError, MirLowerError},
|
||||
object_safety::{MethodViolationCode, ObjectSafetyViolation},
|
||||
CastError, FnAbi, PointerCast, Safety,
|
||||
},
|
||||
// FIXME: Properly encapsulate mir
|
||||
|
|
@ -497,10 +497,9 @@ impl Module {
|
|||
|
||||
/// Finds a parent module.
|
||||
pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
|
||||
// FIXME: handle block expressions as modules (their parent is in a different DefMap)
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
let parent_id = def_map[self.id.local_id].parent?;
|
||||
Some(Module { id: def_map.module_id(parent_id) })
|
||||
let parent_id = def_map.containing_module(self.id.local_id)?;
|
||||
Some(Module { id: parent_id })
|
||||
}
|
||||
|
||||
/// Finds nearest non-block ancestor `Module` (`self` included).
|
||||
|
|
@ -557,7 +556,7 @@ impl Module {
|
|||
acc: &mut Vec<AnyDiagnostic>,
|
||||
style_lints: bool,
|
||||
) {
|
||||
let _p = tracing::info_span!("Module::diagnostics", name = ?self.name(db)).entered();
|
||||
let _p = tracing::info_span!("diagnostics", name = ?self.name(db)).entered();
|
||||
let edition = db.crate_graph()[self.id.krate()].edition;
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
for diag in def_map.diagnostics() {
|
||||
|
|
@ -2690,8 +2689,8 @@ impl Trait {
|
|||
.count()
|
||||
}
|
||||
|
||||
pub fn object_safety(&self, db: &dyn HirDatabase) -> Option<ObjectSafetyViolation> {
|
||||
hir_ty::object_safety::object_safety(db, self.id)
|
||||
pub fn dyn_compatibility(&self, db: &dyn HirDatabase) -> Option<DynCompatibilityViolation> {
|
||||
hir_ty::dyn_compatibility::dyn_compatibility(db, self.id)
|
||||
}
|
||||
|
||||
fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use hir_expand::{
|
|||
builtin::{BuiltinFnLikeExpander, EagerExpander},
|
||||
db::ExpandDatabase,
|
||||
files::InRealFile,
|
||||
hygiene::SyntaxContextExt as _,
|
||||
inert_attr_macro::find_builtin_attr_idx,
|
||||
name::AsName,
|
||||
FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
|
||||
|
|
@ -32,13 +33,13 @@ use intern::Symbol;
|
|||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::{EditionedFileId, FileId, HirFileIdRepr};
|
||||
use span::{EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
|
||||
use stdx::TupleExt;
|
||||
use syntax::{
|
||||
algo::skip_trivia_token,
|
||||
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody, IsString as _},
|
||||
match_ast, AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
|
||||
TextRange, TextSize,
|
||||
ast::{self, HasAttrs as _, HasGenericParams, IsString as _},
|
||||
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
|
||||
TextSize,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -608,7 +609,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let quote = string.open_quote_text_range()?;
|
||||
|
||||
let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
|
||||
self.descend_into_macros_breakable(token, |token| {
|
||||
self.descend_into_macros_breakable(token, |token, _| {
|
||||
(|| {
|
||||
let token = token.value;
|
||||
let string = ast::String::cast(token)?;
|
||||
|
|
@ -655,7 +656,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let original_string = ast::String::cast(original_token.clone())?;
|
||||
let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
|
||||
let quote = original_string.open_quote_text_range()?;
|
||||
self.descend_into_macros_breakable(original_token, |token| {
|
||||
self.descend_into_macros_breakable(original_token, |token, _| {
|
||||
(|| {
|
||||
let token = token.value;
|
||||
self.resolve_offset_in_format_args(
|
||||
|
|
@ -718,7 +719,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
// node is just the token, so descend the token
|
||||
self.descend_into_macros_impl(
|
||||
InRealFile::new(file_id, first),
|
||||
&mut |InFile { value, .. }| {
|
||||
&mut |InFile { value, .. }, _ctx| {
|
||||
if let Some(node) = value
|
||||
.parent_ancestors()
|
||||
.take_while(|it| it.text_range() == value.text_range())
|
||||
|
|
@ -732,7 +733,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
} else {
|
||||
// Descend first and last token, then zip them to look for the node they belong to
|
||||
let mut scratch: SmallVec<[_; 1]> = smallvec![];
|
||||
self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token| {
|
||||
self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token, _ctx| {
|
||||
scratch.push(token);
|
||||
CONTINUE_NO_BREAKS
|
||||
});
|
||||
|
|
@ -740,7 +741,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let mut scratch = scratch.into_iter();
|
||||
self.descend_into_macros_impl(
|
||||
InRealFile::new(file_id, last),
|
||||
&mut |InFile { value: last, file_id: last_fid }| {
|
||||
&mut |InFile { value: last, file_id: last_fid }, _ctx| {
|
||||
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
|
||||
if first_fid == last_fid {
|
||||
if let Some(p) = first.parent() {
|
||||
|
|
@ -763,7 +764,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||
res
|
||||
}
|
||||
|
||||
fn is_inside_macro_call(token: &SyntaxToken) -> bool {
|
||||
// FIXME: This isn't quite right wrt to inner attributes
|
||||
/// Does a syntactic traversal to check whether this token might be inside a macro call
|
||||
pub fn might_be_inside_macro_call(&self, token: &SyntaxToken) -> bool {
|
||||
token.parent_ancestors().any(|ancestor| {
|
||||
if ast::MacroCall::can_cast(ancestor.kind()) {
|
||||
return true;
|
||||
|
|
@ -781,25 +784,14 @@ impl<'db> SemanticsImpl<'db> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn descend_into_macros_exact_if_in_macro(
|
||||
&self,
|
||||
token: SyntaxToken,
|
||||
) -> SmallVec<[SyntaxToken; 1]> {
|
||||
if Self::is_inside_macro_call(&token) {
|
||||
self.descend_into_macros_exact(token)
|
||||
} else {
|
||||
smallvec![token]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn descend_into_macros_cb(
|
||||
&self,
|
||||
token: SyntaxToken,
|
||||
mut cb: impl FnMut(InFile<SyntaxToken>),
|
||||
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContextId),
|
||||
) {
|
||||
if let Ok(token) = self.wrap_token_infile(token).into_real_file() {
|
||||
self.descend_into_macros_impl(token, &mut |t| {
|
||||
cb(t);
|
||||
self.descend_into_macros_impl(token, &mut |t, ctx| {
|
||||
cb(t, ctx);
|
||||
CONTINUE_NO_BREAKS
|
||||
});
|
||||
}
|
||||
|
|
@ -808,7 +800,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
|
||||
let mut res = smallvec![];
|
||||
if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
|
||||
self.descend_into_macros_impl(token, &mut |t| {
|
||||
self.descend_into_macros_impl(token, &mut |t, _ctx| {
|
||||
res.push(t.value);
|
||||
CONTINUE_NO_BREAKS
|
||||
});
|
||||
|
|
@ -819,10 +811,27 @@ impl<'db> SemanticsImpl<'db> {
|
|||
res
|
||||
}
|
||||
|
||||
pub fn descend_into_macros_no_opaque(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
|
||||
let mut res = smallvec![];
|
||||
if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
|
||||
self.descend_into_macros_impl(token, &mut |t, ctx| {
|
||||
if !ctx.is_opaque(self.db.upcast()) {
|
||||
// Don't descend into opaque contexts
|
||||
res.push(t.value);
|
||||
}
|
||||
CONTINUE_NO_BREAKS
|
||||
});
|
||||
}
|
||||
if res.is_empty() {
|
||||
res.push(token);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn descend_into_macros_breakable<T>(
|
||||
&self,
|
||||
token: InRealFile<SyntaxToken>,
|
||||
mut cb: impl FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
|
||||
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContextId) -> ControlFlow<T>,
|
||||
) -> Option<T> {
|
||||
self.descend_into_macros_impl(token.clone(), &mut cb)
|
||||
}
|
||||
|
|
@ -834,10 +843,12 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let text = token.text();
|
||||
let kind = token.kind();
|
||||
|
||||
self.descend_into_macros_cb(token.clone(), |InFile { value, file_id: _ }| {
|
||||
self.descend_into_macros_cb(token.clone(), |InFile { value, file_id: _ }, ctx| {
|
||||
let mapped_kind = value.kind();
|
||||
let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
|
||||
let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
|
||||
let matches = (kind == mapped_kind || any_ident_match())
|
||||
&& text == value.text()
|
||||
&& !ctx.is_opaque(self.db.upcast());
|
||||
if matches {
|
||||
r.push(value);
|
||||
}
|
||||
|
|
@ -854,17 +865,21 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let text = token.text();
|
||||
let kind = token.kind();
|
||||
if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
|
||||
self.descend_into_macros_breakable(token.clone(), |InFile { value, file_id: _ }| {
|
||||
let mapped_kind = value.kind();
|
||||
let any_ident_match =
|
||||
|| kind.is_any_identifier() && value.kind().is_any_identifier();
|
||||
let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
|
||||
if matches {
|
||||
ControlFlow::Break(value)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
})
|
||||
self.descend_into_macros_breakable(
|
||||
token.clone(),
|
||||
|InFile { value, file_id: _ }, _ctx| {
|
||||
let mapped_kind = value.kind();
|
||||
let any_ident_match =
|
||||
|| kind.is_any_identifier() && value.kind().is_any_identifier();
|
||||
let matches =
|
||||
(kind == mapped_kind || any_ident_match()) && text == value.text();
|
||||
if matches {
|
||||
ControlFlow::Break(value)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -874,7 +889,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
fn descend_into_macros_impl<T>(
|
||||
&self,
|
||||
InRealFile { value: token, file_id }: InRealFile<SyntaxToken>,
|
||||
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
|
||||
f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContextId) -> ControlFlow<T>,
|
||||
) -> Option<T> {
|
||||
let _p = tracing::info_span!("descend_into_macros_impl").entered();
|
||||
let (sa, span, file_id) = token
|
||||
|
|
@ -898,7 +913,8 @@ impl<'db> SemanticsImpl<'db> {
|
|||
// These are tracked to know which macro calls we still have to look into
|
||||
// the tokens themselves aren't that interesting as the span that is being used to map
|
||||
// things down never changes.
|
||||
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
|
||||
let mut stack: Vec<(_, SmallVec<[_; 2]>)> =
|
||||
vec![(file_id, smallvec![(token, SyntaxContextId::ROOT)])];
|
||||
|
||||
// Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
|
||||
let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
||||
|
|
@ -921,11 +937,11 @@ impl<'db> SemanticsImpl<'db> {
|
|||
// Filters out all tokens that contain the given range (usually the macro call), any such
|
||||
// token is redundant as the corresponding macro call has already been processed
|
||||
let filter_duplicates = |tokens: &mut SmallVec<_>, range: TextRange| {
|
||||
tokens.retain(|t: &mut SyntaxToken| !range.contains_range(t.text_range()))
|
||||
tokens.retain(|(t, _): &mut (SyntaxToken, _)| !range.contains_range(t.text_range()))
|
||||
};
|
||||
|
||||
while let Some((expansion, ref mut tokens)) = stack.pop() {
|
||||
while let Some(token) = tokens.pop() {
|
||||
while let Some((token, ctx)) = tokens.pop() {
|
||||
let was_not_remapped = (|| {
|
||||
// First expand into attribute invocations
|
||||
let containing_attribute_macro_call = self.with_ctx(|ctx| {
|
||||
|
|
@ -1036,7 +1052,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let text_range = attr.syntax().text_range();
|
||||
// remove any other token in this macro input, all their mappings are the
|
||||
// same as this
|
||||
tokens.retain(|t| {
|
||||
tokens.retain(|(t, _)| {
|
||||
!text_range.contains_range(t.text_range())
|
||||
});
|
||||
return process_expansion_for_token(
|
||||
|
|
@ -1093,7 +1109,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
.is_none();
|
||||
|
||||
if was_not_remapped {
|
||||
if let ControlFlow::Break(b) = f(InFile::new(expansion, token)) {
|
||||
if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) {
|
||||
return Some(b);
|
||||
}
|
||||
}
|
||||
|
|
@ -1221,26 +1237,10 @@ impl<'db> SemanticsImpl<'db> {
|
|||
ToDef::to_def(self, src.as_ref())
|
||||
}
|
||||
|
||||
pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
|
||||
let text = lifetime.text();
|
||||
let label = lifetime.syntax().ancestors().find_map(|syn| {
|
||||
let label = match_ast! {
|
||||
match syn {
|
||||
ast::ForExpr(it) => it.label(),
|
||||
ast::WhileExpr(it) => it.label(),
|
||||
ast::LoopExpr(it) => it.label(),
|
||||
ast::BlockExpr(it) => it.label(),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
label.filter(|l| {
|
||||
l.lifetime()
|
||||
.and_then(|lt| lt.lifetime_ident_token())
|
||||
.map_or(false, |lt| lt.text() == text)
|
||||
})
|
||||
})?;
|
||||
let src = self.wrap_node_infile(label);
|
||||
ToDef::to_def(self, src.as_ref())
|
||||
pub fn resolve_label(&self, label: &ast::Lifetime) -> Option<Label> {
|
||||
let (parent, label_id) = self
|
||||
.with_ctx(|ctx| ctx.label_ref_to_def(self.wrap_node_infile(label.clone()).as_ref()))?;
|
||||
Some(Label { parent, label_id })
|
||||
}
|
||||
|
||||
pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ use hir_def::{
|
|||
keys::{self, Key},
|
||||
DynMap,
|
||||
},
|
||||
hir::{BindingId, LabelId},
|
||||
hir::{BindingId, Expr, LabelId},
|
||||
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
|
||||
FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
|
||||
ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
|
||||
|
|
@ -343,6 +343,20 @@ impl SourceToDefCtx<'_, '_> {
|
|||
Some((container, label_id))
|
||||
}
|
||||
|
||||
pub(super) fn label_ref_to_def(
|
||||
&mut self,
|
||||
src: InFile<&ast::Lifetime>,
|
||||
) -> Option<(DefWithBodyId, LabelId)> {
|
||||
let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
|
||||
let container = self.find_pat_or_label_container(src.syntax_ref())?;
|
||||
let (body, source_map) = self.db.body_with_source_map(container);
|
||||
let break_or_continue = source_map.node_expr(src.with_value(&break_or_continue))?;
|
||||
let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
|
||||
return None;
|
||||
};
|
||||
Some((container, label?))
|
||||
}
|
||||
|
||||
pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
|
||||
let map = self.dyn_map(src)?;
|
||||
map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied()
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue