Merge from rustc

This commit is contained in:
The Miri Conjob Bot 2023-08-12 05:33:13 +00:00
commit b122fa6d6a
194 changed files with 2663 additions and 767 deletions

View file

@ -458,6 +458,7 @@ Philipp Matthias Schäfer <philipp.matthias.schaefer@posteo.de>
phosphorus <steepout@qq.com>
Pierre Krieger <pierre.krieger1708@gmail.com>
pierwill <pierwill@users.noreply.github.com> <19642016+pierwill@users.noreply.github.com>
Pietro Albini <pietro@pietroalbini.org> <pietro@pietroalbini.io> <pietro.albini@ferrous-systems.com>
Pradyumna Rahul <prkinformed@gmail.com>
Przemysław Wesołek <jest@go.art.pl> Przemek Wesołek <jest@go.art.pl>
r00ster <r00ster91@protonmail.com>
@ -495,6 +496,8 @@ Ryan Wiedemann <Ryan1729@gmail.com>
S Pradeep Kumar <gohanpra@gmail.com>
Sam Radhakrishnan <sk09idm@gmail.com>
Samuel Tardieu <sam@rfc1149.net>
Santiago Pastorino <spastorino@gmail.com>
Santiago Pastorino <spastorino@gmail.com> <santiago@wyeworks.com>
Scott McMurray <scottmcm@users.noreply.github.com>
Scott Olson <scott@solson.me> Scott Olson <scott@scott-olson.org>
Sean Gillespie <sean.william.g@gmail.com> swgillespie <sean.william.g@gmail.com>

View file

@ -457,7 +457,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
// Don't hash unless necessary, because it's expensive.
let opt_hir_hash =
if tcx.sess.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None };
if tcx.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None };
hir::Crate { owners, opt_hir_hash }
}
@ -648,7 +648,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let bodies = SortedMap::from_presorted_elements(bodies);
// Don't hash unless necessary, because it's expensive.
let (opt_hash_including_bodies, attrs_hash) = if self.tcx.sess.needs_crate_hash() {
let (opt_hash_including_bodies, attrs_hash) = if self.tcx.needs_crate_hash() {
self.tcx.with_stable_hashing_context(|mut hcx| {
let mut stable_hasher = StableHasher::new();
hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| {

View file

@ -239,5 +239,10 @@ ast_passes_visibility_not_permitted =
.individual_impl_items = place qualifiers on individual impl items instead
.individual_foreign_items = place qualifiers on individual foreign items instead
ast_passes_where_after_type_alias = where clauses are not allowed after the type for type aliases
ast_passes_where_clause_after_type_alias = where clauses are not allowed after the type for type aliases
.note = see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
.help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable
ast_passes_where_clause_before_type_alias = where clauses are not allowed before the type for type aliases
.note = see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information
.suggestion = move it to the end of the type declaration

View file

@ -136,40 +136,42 @@ impl<'a> AstValidator<'a> {
}
}
fn check_gat_where(
fn check_type_alias_where_clause_location(
&mut self,
id: NodeId,
before_predicates: &[WherePredicate],
where_clauses: (ast::TyAliasWhereClause, ast::TyAliasWhereClause),
) {
if !before_predicates.is_empty() {
let mut state = State::new();
if !where_clauses.1.0 {
state.space();
state.word_space("where");
} else {
ty_alias: &TyAlias,
) -> Result<(), errors::WhereClauseBeforeTypeAlias> {
let before_predicates =
ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_predicates_split).0;
if ty_alias.ty.is_none() || before_predicates.is_empty() {
return Ok(());
}
let mut state = State::new();
if !ty_alias.where_clauses.1.0 {
state.space();
state.word_space("where");
} else {
state.word_space(",");
}
let mut first = true;
for p in before_predicates {
if !first {
state.word_space(",");
}
let mut first = true;
for p in before_predicates.iter() {
if !first {
state.word_space(",");
}
first = false;
state.print_where_predicate(p);
}
let suggestion = state.s.eof();
self.lint_buffer.buffer_lint_with_diagnostic(
DEPRECATED_WHERE_CLAUSE_LOCATION,
id,
where_clauses.0.1,
fluent::ast_passes_deprecated_where_clause_location,
BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(
where_clauses.1.1.shrink_to_hi(),
suggestion,
),
);
first = false;
state.print_where_predicate(p);
}
let span = ty_alias.where_clauses.0.1;
Err(errors::WhereClauseBeforeTypeAlias {
span,
sugg: errors::WhereClauseBeforeTypeAliasSugg {
left: span,
snippet: state.s.eof(),
right: ty_alias.where_clauses.1.1.shrink_to_hi(),
},
})
}
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
@ -1009,7 +1011,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
replace_span: self.ending_semi_or_hi(item.span),
});
}
ItemKind::TyAlias(box TyAlias { defaultness, where_clauses, bounds, ty, .. }) => {
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness);
if ty.is_none() {
self.session.emit_err(errors::TyAliasWithoutBody {
@ -1018,9 +1022,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
});
}
self.check_type_no_bounds(bounds, "this context");
if where_clauses.1.0 {
self.err_handler()
.emit_err(errors::WhereAfterTypeAlias { span: where_clauses.1.1 });
if self.session.features_untracked().lazy_type_alias {
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
self.err_handler().emit_err(err);
}
} else if where_clauses.1.0 {
self.err_handler().emit_err(errors::WhereClauseAfterTypeAlias {
span: where_clauses.1.1,
help: self.session.is_nightly_build().then_some(()),
});
}
}
_ => {}
@ -1313,18 +1324,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
if let AssocItemKind::Type(box TyAlias {
generics,
where_clauses,
where_predicates_split,
ty: Some(_),
..
}) = &item.kind
if let AssocItemKind::Type(ty_alias) = &item.kind
&& let Err(err) = self.check_type_alias_where_clause_location(ty_alias)
{
self.check_gat_where(
self.lint_buffer.buffer_lint_with_diagnostic(
DEPRECATED_WHERE_CLAUSE_LOCATION,
item.id,
generics.where_clause.predicates.split_at(*where_predicates_split).0,
*where_clauses,
err.span,
fluent::ast_passes_deprecated_where_clause_location,
BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(
err.sugg.right,
err.sugg.snippet,
),
);
}

View file

@ -496,11 +496,37 @@ pub struct FieldlessUnion {
}
#[derive(Diagnostic)]
#[diag(ast_passes_where_after_type_alias)]
#[diag(ast_passes_where_clause_after_type_alias)]
#[note]
pub struct WhereAfterTypeAlias {
pub struct WhereClauseAfterTypeAlias {
#[primary_span]
pub span: Span,
#[help]
pub help: Option<()>,
}
#[derive(Diagnostic)]
#[diag(ast_passes_where_clause_before_type_alias)]
#[note]
pub struct WhereClauseBeforeTypeAlias {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: WhereClauseBeforeTypeAliasSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
ast_passes_suggestion,
applicability = "machine-applicable",
style = "verbose"
)]
pub struct WhereClauseBeforeTypeAliasSugg {
#[suggestion_part(code = "")]
pub left: Span,
pub snippet: String,
#[suggestion_part(code = "{snippet}")]
pub right: Span,
}
#[derive(Diagnostic)]

View file

@ -1,6 +1,6 @@
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{token, StmtKind};
use rustc_ast::{
Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs,
FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount,
@ -163,7 +163,7 @@ fn make_format_args(
let MacroInput { fmtstr: efmt, mut args, is_direct_literal } = input;
let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) {
let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt.clone(), msg) {
Ok(mut fmt) if append_newline => {
fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
fmt
@ -171,17 +171,33 @@ fn make_format_args(
Ok(fmt) => fmt,
Err(err) => {
if let Some((mut err, suggested)) = err {
let sugg_fmt = match args.explicit_args().len() {
0 => "{}".to_string(),
_ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())),
};
if !suggested {
err.span_suggestion(
unexpanded_fmt_span.shrink_to_lo(),
"you might be missing a string literal to format with",
format!("\"{sugg_fmt}\", "),
Applicability::MaybeIncorrect,
);
if let ExprKind::Block(block, None) = &efmt.kind
&& block.stmts.len() == 1
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
&& let ExprKind::Path(None, path) = &expr.kind
&& path.is_potential_trivial_const_arg()
{
err.multipart_suggestion(
"quote your inlined format argument to use as string literal",
vec![
(unexpanded_fmt_span.shrink_to_hi(), "\"".to_string()),
(unexpanded_fmt_span.shrink_to_lo(), "\"".to_string()),
],
Applicability::MaybeIncorrect,
);
} else {
let sugg_fmt = match args.explicit_args().len() {
0 => "{}".to_string(),
_ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())),
};
err.span_suggestion(
unexpanded_fmt_span.shrink_to_lo(),
"you might be missing a string literal to format with",
format!("\"{sugg_fmt}\", "),
Applicability::MaybeIncorrect,
);
}
}
err.emit();
}

View file

@ -98,7 +98,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
tcx.sess.fatal("JIT mode doesn't work with `cargo check`");
}
if !tcx.sess.crate_types().contains(&rustc_session::config::CrateType::Executable) {
if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) {
tcx.sess.fatal("can't jit non-executable crate");
}

View file

@ -209,7 +209,7 @@ pub unsafe fn create_module<'ll>(
// PIE is potentially more effective than PIC, but can only be used in executables.
// If all our outputs are executables, then we can relax PIC to PIE.
if reloc_model == RelocModel::Pie
|| sess.crate_types().iter().all(|ty| *ty == CrateType::Executable)
|| tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable)
{
llvm::LLVMRustSetModulePIELevel(llmod);
}

View file

@ -12,8 +12,7 @@ use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_middle::ty::TyCtxt;
use std::ffi::CString;
use rustc_span::Symbol;
/// Generates and exports the Coverage Map.
///
@ -89,7 +88,10 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
// Encode all filenames referenced by counters/expressions in this module
let filenames_buffer = llvm::build_byte_buffer(|filenames_buffer| {
coverageinfo::write_filenames_section_to_buffer(&mapgen.filenames, filenames_buffer);
coverageinfo::write_filenames_section_to_buffer(
mapgen.filenames.iter().map(Symbol::as_str),
filenames_buffer,
);
});
let filenames_size = filenames_buffer.len();
@ -117,7 +119,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
}
struct CoverageMapGenerator {
filenames: FxIndexSet<CString>,
filenames: FxIndexSet<Symbol>,
}
impl CoverageMapGenerator {
@ -128,11 +130,10 @@ impl CoverageMapGenerator {
// Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the relative paths
// to get absolute paths, if needed.
let working_dir =
tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy().to_string();
let c_filename =
CString::new(working_dir).expect("null error converting filename to C string");
filenames.insert(c_filename);
let working_dir = Symbol::intern(
&tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy(),
);
filenames.insert(working_dir);
Self { filenames }
}
@ -170,10 +171,8 @@ impl CoverageMapGenerator {
current_file_id += 1;
}
current_file_name = Some(file_name);
let c_filename = CString::new(file_name.to_string())
.expect("null error converting filename to C string");
debug!(" file_id: {} = '{:?}'", current_file_id, c_filename);
let (filenames_index, _) = self.filenames.insert_full(c_filename);
debug!(" file_id: {} = '{:?}'", current_file_id, file_name);
let (filenames_index, _) = self.filenames.insert_full(file_name);
virtual_file_mapping.push(filenames_index as u32);
}
debug!("Adding counter {:?} to map for {:?}", counter, region);

View file

@ -25,7 +25,6 @@ use rustc_middle::ty::Instance;
use rustc_middle::ty::Ty;
use std::cell::RefCell;
use std::ffi::CString;
pub(crate) mod ffi;
pub(crate) mod map_data;
@ -332,21 +331,32 @@ fn create_pgo_func_name_var<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
instance: Instance<'tcx>,
) -> &'ll llvm::Value {
let mangled_fn_name = CString::new(cx.tcx.symbol_name(instance).name)
.expect("error converting function name to C string");
let mangled_fn_name: &str = cx.tcx.symbol_name(instance).name;
let llfn = cx.get_fn(instance);
unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
unsafe {
llvm::LLVMRustCoverageCreatePGOFuncNameVar(
llfn,
mangled_fn_name.as_ptr().cast(),
mangled_fn_name.len(),
)
}
}
pub(crate) fn write_filenames_section_to_buffer<'a>(
filenames: impl IntoIterator<Item = &'a CString>,
filenames: impl IntoIterator<Item = &'a str>,
buffer: &RustString,
) {
let c_str_vec = filenames.into_iter().map(|cstring| cstring.as_ptr()).collect::<Vec<_>>();
let (pointers, lengths) = filenames
.into_iter()
.map(|s: &str| (s.as_ptr().cast(), s.len()))
.unzip::<_, _, Vec<_>, Vec<_>>();
unsafe {
llvm::LLVMRustCoverageWriteFilenamesSectionToBuffer(
c_str_vec.as_ptr(),
c_str_vec.len(),
pointers.as_ptr(),
pointers.len(),
lengths.as_ptr(),
lengths.len(),
buffer,
);
}

View file

@ -92,7 +92,7 @@ pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
// each rlib could produce a different set of visualizers that would be embedded
// in the `.debug_gdb_scripts` section. For that reason, we make sure that the
// section is only emitted for leaf crates.
let embed_visualizers = cx.sess().crate_types().iter().any(|&crate_type| match crate_type {
let embed_visualizers = cx.tcx.crate_types().iter().any(|&crate_type| match crate_type {
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => {
// These are crate types for which we will embed pretty printers since they
// are treated as leaf crates.

View file

@ -1707,6 +1707,8 @@ extern "C" {
pub fn LLVMRustCoverageWriteFilenamesSectionToBuffer(
Filenames: *const *const c_char,
FilenamesLen: size_t,
Lengths: *const size_t,
LengthsLen: size_t,
BufferOut: &RustString,
);
@ -1721,7 +1723,11 @@ extern "C" {
BufferOut: &RustString,
);
pub fn LLVMRustCoverageCreatePGOFuncNameVar(F: &Value, FuncName: *const c_char) -> &Value;
pub fn LLVMRustCoverageCreatePGOFuncNameVar(
F: &Value,
FuncName: *const c_char,
FuncNameLen: size_t,
) -> &Value;
pub fn LLVMRustCoverageHashByteArray(Bytes: *const c_char, NumBytes: size_t) -> u64;
#[allow(improper_ctypes)]

View file

@ -111,7 +111,7 @@ impl CodegenCx<'_, '_> {
}
// Symbols from executables can't really be imported any further.
let all_exe = self.tcx.sess.crate_types().iter().all(|ty| *ty == CrateType::Executable);
let all_exe = self.tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable);
let is_declaration_for_linker =
is_declaration || linkage == llvm::Linkage::AvailableExternallyLinkage;
if all_exe && !is_declaration_for_linker {

View file

@ -69,7 +69,7 @@ pub fn link_binary<'a>(
let _timer = sess.timer("link_binary");
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
for &crate_type in sess.crate_types().iter() {
for &crate_type in &codegen_results.crate_info.crate_types {
// Ignore executable crates if we have -Z no-codegen, as they will error.
if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
&& !output_metadata

View file

@ -1703,7 +1703,7 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
return Vec::new();
}
let stable_crate_id = tcx.sess.local_stable_crate_id();
let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);

View file

@ -19,7 +19,7 @@ use rustc_session::config::{CrateType, OomStrategy};
use rustc_target::spec::SanitizerSet;
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
crates_export_threshold(&tcx.sess.crate_types())
crates_export_threshold(tcx.crate_types())
}
fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel {
@ -290,8 +290,8 @@ fn exported_symbols_provider_local(
}));
}
if tcx.sess.crate_types().contains(&CrateType::Dylib)
|| tcx.sess.crate_types().contains(&CrateType::ProcMacro)
if tcx.crate_types().contains(&CrateType::Dylib)
|| tcx.crate_types().contains(&CrateType::ProcMacro)
{
let symbol_name = metadata_symbol_name(tcx);
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));

View file

@ -123,7 +123,7 @@ pub struct ModuleConfig {
impl ModuleConfig {
fn new(
kind: ModuleKind,
sess: &Session,
tcx: TyCtxt<'_>,
no_builtins: bool,
is_compiler_builtins: bool,
) -> ModuleConfig {
@ -135,6 +135,7 @@ impl ModuleConfig {
};
}
let sess = tcx.sess;
let opt_level_and_size = if_regular!(Some(sess.opts.optimize), None);
let save_temps = sess.opts.cg.save_temps;
@ -166,7 +167,7 @@ impl ModuleConfig {
// `#![no_builtins]` is assumed to not participate in LTO and
// instead goes on to generate object code.
EmitObj::Bitcode
} else if need_bitcode_in_object(sess) {
} else if need_bitcode_in_object(tcx) {
EmitObj::ObjectCode(BitcodeSection::Full)
} else {
EmitObj::ObjectCode(BitcodeSection::None)
@ -414,9 +415,10 @@ pub struct CompiledModules {
pub allocator_module: Option<CompiledModule>,
}
fn need_bitcode_in_object(sess: &Session) -> bool {
fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool {
let sess = tcx.sess;
let requested_for_rlib = sess.opts.cg.embed_bitcode
&& sess.crate_types().contains(&CrateType::Rlib)
&& tcx.crate_types().contains(&CrateType::Rlib)
&& sess.opts.output_types.contains_key(&OutputType::Exe);
let forced_by_target = sess.target.forces_embed_bitcode;
requested_for_rlib || forced_by_target
@ -450,11 +452,11 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
let crate_info = CrateInfo::new(tcx, target_cpu);
let regular_config =
ModuleConfig::new(ModuleKind::Regular, sess, no_builtins, is_compiler_builtins);
ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins, is_compiler_builtins);
let metadata_config =
ModuleConfig::new(ModuleKind::Metadata, sess, no_builtins, is_compiler_builtins);
ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins, is_compiler_builtins);
let allocator_config =
ModuleConfig::new(ModuleKind::Allocator, sess, no_builtins, is_compiler_builtins);
ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins, is_compiler_builtins);
let (shared_emitter, shared_emitter_main) = SharedEmitter::new();
let (codegen_worker_send, codegen_worker_receive) = channel();
@ -1092,7 +1094,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
};
let cgcx = CodegenContext::<B> {
crate_types: sess.crate_types().to_vec(),
crate_types: tcx.crate_types().to_vec(),
each_linked_rlib_for_lto,
lto: sess.lto(),
fewer_names: sess.fewer_names(),
@ -2063,7 +2065,7 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool {
);
tcx.sess.target.is_like_windows &&
tcx.sess.crate_types().iter().any(|ct| *ct == CrateType::Rlib) &&
tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib) &&
// ThinLTO can't handle this workaround in all cases, so we don't
// emit the `__imp_` symbols. Instead we make them unnecessary by disallowing
// dynamic linking when linker plugin LTO is enabled.

View file

@ -779,18 +779,13 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
impl CrateInfo {
pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
let exported_symbols = tcx
.sess
.crate_types()
let crate_types = tcx.crate_types().to_vec();
let exported_symbols = crate_types
.iter()
.map(|&c| (c, crate::back::linker::exported_symbols(tcx, c)))
.collect();
let linked_symbols = tcx
.sess
.crate_types()
.iter()
.map(|&c| (c, crate::back::linker::linked_symbols(tcx, c)))
.collect();
let linked_symbols =
crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect();
let local_crate_name = tcx.crate_name(LOCAL_CRATE);
let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
let subsystem = attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem);
@ -829,6 +824,7 @@ impl CrateInfo {
let mut info = CrateInfo {
target_cpu,
crate_types,
exported_symbols,
linked_symbols,
local_crate_name,
@ -916,7 +912,7 @@ impl CrateInfo {
});
}
let embed_visualizers = tcx.sess.crate_types().iter().any(|&crate_type| match crate_type {
let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type {
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => {
// These are crate types for which we invoke the linker and can embed
// NatVis visualizers.
@ -1013,7 +1009,7 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR
match compute_per_cgu_lto_type(
&tcx.sess.lto(),
&tcx.sess.opts,
&tcx.sess.crate_types(),
tcx.crate_types(),
ModuleKind::Regular,
) {
ComputedLtoType::No => CguReuse::PostLto,

View file

@ -150,6 +150,7 @@ impl From<&cstore::NativeLib> for NativeLib {
#[derive(Debug, Encodable, Decodable)]
pub struct CrateInfo {
pub target_cpu: String,
pub crate_types: Vec<CrateType>,
pub exported_symbols: FxHashMap<CrateType, Vec<String>>,
pub linked_symbols: FxHashMap<CrateType, Vec<(String, SymbolExportKind)>>,
pub local_crate_name: Symbol,

View file

@ -653,8 +653,6 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) {
pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
if sess.opts.unstable_opts.link_only {
if let Input::File(file) = &sess.io.input {
// FIXME: #![crate_type] and #![crate_name] support not implemented yet
sess.init_crate_types(collect_crate_types(sess, &[]));
let outputs = compiler.build_output_filenames(sess, &[]);
let rlink_data = fs::read(file).unwrap_or_else(|err| {
sess.emit_fatal(RlinkUnableToRead { err });

View file

@ -1843,7 +1843,7 @@ impl Expr<'_> {
.iter()
.map(|field| field.expr)
.chain(init.into_iter())
.all(|e| e.can_have_side_effects()),
.any(|e| e.can_have_side_effects()),
ExprKind::Array(args)
| ExprKind::Tup(args)
@ -1857,7 +1857,7 @@ impl Expr<'_> {
..
},
args,
) => args.iter().all(|arg| arg.can_have_side_effects()),
) => args.iter().any(|arg| arg.can_have_side_effects()),
ExprKind::If(..)
| ExprKind::Match(..)
| ExprKind::MethodCall(..)

View file

@ -1,7 +1,7 @@
use super::potentially_plural_count;
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
use hir::def_id::{DefId, LocalDefId};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_errors::{
pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan,
};
@ -265,7 +265,6 @@ fn compare_method_predicate_entailment<'tcx>(
infer::HigherRankedType,
tcx.fn_sig(impl_m.def_id).instantiate_identity(),
);
let unnormalized_impl_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig));
let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id);
let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig);
@ -309,16 +308,44 @@ fn compare_method_predicate_entailment<'tcx>(
}
if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() {
// We need to check that the impl's args are well-formed given
// the hybrid param-env (impl + trait method where-clauses).
ocx.register_obligation(traits::Obligation::new(
infcx.tcx,
ObligationCause::dummy(),
param_env,
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
unnormalized_impl_fty.into(),
))),
));
// See #108544. Annoying, we can end up in cases where, because of winnowing,
// we pick param env candidates over a more general impl, leading to more
// stricter lifetime requirements than we would otherwise need. This can
// trigger the lint. Instead, let's only consider type outlives and
// region outlives obligations.
//
// FIXME(-Ztrait-solver=next): Try removing this hack again once
// the new solver is stable.
let mut wf_args: smallvec::SmallVec<[_; 4]> =
unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect();
// Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?))
// will give back the well-formed predicate of the same array.
let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect();
while let Some(arg) = wf_args.pop() {
let Some(obligations) = rustc_trait_selection::traits::wf::obligations(
infcx,
param_env,
impl_m_def_id,
0,
arg,
impl_m_span,
) else {
continue;
};
for obligation in obligations {
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(
ty::ClauseKind::RegionOutlives(..) | ty::ClauseKind::TypeOutlives(..),
) => ocx.register_obligation(obligation),
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
if wf_args_seen.insert(arg) {
wf_args.push(arg)
}
}
_ => {}
}
}
}
}
// Check that all obligations are satisfied by the implementation's

View file

@ -645,18 +645,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
}
hir::ExprKind::Call(ref inner_callee, _) => {
// If the call spans more than one line and the callee kind is
// itself another `ExprCall`, that's a clue that we might just be
// missing a semicolon (Issue #51055)
let call_is_multiline = self.tcx.sess.source_map().is_multiline(call_expr.span);
if call_is_multiline {
err.span_suggestion(
callee_expr.span.shrink_to_hi(),
"consider using a semicolon here",
";",
Applicability::MaybeIncorrect,
);
}
if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind {
inner_callee_path = Some(inner_qpath);
self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id)
@ -668,6 +656,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
// If the call spans more than one line and the callee kind is
// itself another `ExprCall`, that's a clue that we might just be
// missing a semicolon (#51055, #106515).
let call_is_multiline = self
.tcx
.sess
.source_map()
.is_multiline(call_expr.span.with_lo(callee_expr.span.hi()))
&& call_expr.span.ctxt() == callee_expr.span.ctxt();
if call_is_multiline {
err.span_suggestion(
callee_expr.span.shrink_to_hi(),
"consider using a semicolon here to finish the statement",
";",
Applicability::MaybeIncorrect,
);
}
if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty)
&& !self.type_is_sized_modulo_regions(self.param_env, output_ty)
{

View file

@ -510,9 +510,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
success(adjustments, ty, obligations)
}
// &[T; n] or &mut [T; n] -> &[T]
// or &mut [T; n] -> &mut [T]
// or &Concrete -> &Trait, etc.
/// Performs [unsized coercion] by emulating a fulfillment loop on a
/// `CoerceUnsized` goal until all `CoerceUnsized` and `Unsize` goals
/// are successfully selected.
///
/// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions)
#[instrument(skip(self), level = "debug")]
fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
source = self.shallow_resolve(source);
@ -1039,6 +1041,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Returns false if the coercion creates any obligations that result in
/// errors.
pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
// FIXME(-Ztrait-solver=next): We need to structurally resolve both types here.
let source = self.resolve_vars_with_obligations(expr_ty);
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);

View file

@ -260,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
));
let expr = expr.peel_drop_temps();
let cause = self.misc(expr.span);
let expr_ty = self.resolve_vars_with_obligations(checked_ty);
let expr_ty = self.resolve_vars_if_possible(checked_ty);
let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e);
let is_insufficiently_polymorphic =

View file

@ -85,16 +85,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// to get more type information.
// FIXME(-Ztrait-solver=next): A lot of the calls to this method should
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
#[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)]
pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
&self,
mut ty: Ty<'tcx>,
mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
) -> Ty<'tcx> {
#[instrument(skip(self), level = "debug", ret)]
pub(in super::super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
// No Infer()? Nothing needs doing.
if !ty.has_non_region_infer() {
debug!("no inference var, nothing needs doing");
@ -112,7 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// possible. This can help substantially when there are
// indirect dependencies that don't seem worth tracking
// precisely.
self.select_obligations_where_possible(mutate_fulfillment_errors);
self.select_obligations_where_possible(|_| {});
self.resolve_vars_if_possible(ty)
}
@ -626,8 +618,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match *self_ty.kind() {
ty::Infer(ty::TyVar(found_vid)) => {
// FIXME: consider using `sub_root_var` here so we
// can see through subtyping.
let found_vid = self.root_var(found_vid);
debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
expected_vid == found_vid
@ -642,8 +632,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_ty: ty::TyVid,
) -> impl DoubleEndedIterator<Item = traits::PredicateObligation<'tcx>> + Captures<'tcx> + 'b
{
// FIXME: consider using `sub_root_var` here so we
// can see through subtyping.
let ty_var_root = self.root_var(self_ty);
trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations());

View file

@ -950,7 +950,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !expected.is_unit() {
return;
}
let found = self.resolve_vars_with_obligations(found);
let found = self.resolve_vars_if_possible(found);
let in_loop = self.is_loop(id)
|| self

View file

@ -2994,7 +2994,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This occurs for UFCS desugaring of `T::method`, where there is no
// receiver expression for the method call, and thus no autoderef.
if let SelfSource::QPath(_) = source {
return is_local(self.resolve_vars_with_obligations(rcvr_ty));
return is_local(rcvr_ty);
}
self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))

View file

@ -12,7 +12,7 @@ use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
use rustc_errors::PResult;
use rustc_expand::base::{ExtCtxt, LintStoreExpand};
use rustc_fs_util::try_canonicalize;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE};
use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore};
use rustc_metadata::creader::CStore;
use rustc_middle::arena::Arena;
@ -248,7 +248,7 @@ fn configure_and_expand(
rustc_ast_passes::ast_validation::check_crate(sess, &krate, resolver.lint_buffer())
});
let crate_types = sess.crate_types();
let crate_types = tcx.crate_types();
let is_executable_crate = crate_types.contains(&CrateType::Executable);
let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
@ -340,11 +340,12 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
// Returns all the paths that correspond to generated files.
fn generated_output_paths(
sess: &Session,
tcx: TyCtxt<'_>,
outputs: &OutputFilenames,
exact_name: bool,
crate_name: Symbol,
) -> Vec<PathBuf> {
let sess = tcx.sess;
let mut out_filenames = Vec::new();
for output_type in sess.opts.output_types.keys() {
let out_filename = outputs.path(*output_type);
@ -353,7 +354,7 @@ fn generated_output_paths(
// If the filename has been overridden using `-o`, it will not be modified
// by appending `.rlib`, `.exe`, etc., so we can skip this transformation.
OutputType::Exe if !exact_name => {
for crate_type in sess.crate_types().iter() {
for crate_type in tcx.crate_types().iter() {
let p = filename_for_input(sess, *crate_type, crate_name, outputs);
out_filenames.push(p.as_path().to_path_buf());
}
@ -586,7 +587,7 @@ fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc<OutputFilenames> {
let outputs = util::build_output_filenames(&krate.attrs, sess);
let output_paths =
generated_output_paths(sess, &outputs, sess.io.output_file.is_some(), crate_name);
generated_output_paths(tcx, &outputs, sess.io.output_file.is_some(), crate_name);
// Ensure the source file isn't accidentally overwritten during compilation.
if let Some(ref input_path) = sess.io.input.opt_path() {
@ -664,6 +665,8 @@ pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock<ExternProviders> = LazyLock:
pub fn create_global_ctxt<'tcx>(
compiler: &'tcx Compiler,
crate_types: Vec<CrateType>,
stable_crate_id: StableCrateId,
lint_store: Lrc<LintStore>,
dep_graph: DepGraph,
untracked: Untracked,
@ -696,6 +699,8 @@ pub fn create_global_ctxt<'tcx>(
gcx_cell.get_or_init(move || {
TyCtxt::create_global_ctxt(
sess,
crate_types,
stable_crate_id,
lint_store,
arena,
hir_arena,

View file

@ -234,13 +234,13 @@ impl<'tcx> Queries<'tcx> {
debug_assert_eq!(_id, CRATE_DEF_ID);
let untracked = Untracked { cstore, source_span, definitions };
// FIXME: Move these fields from session to tcx and make them immutable.
sess.init_crate_types(crate_types);
sess.stable_crate_id.set(stable_crate_id).expect("not yet initialized");
// FIXME: Move features from session to tcx and make them immutable.
sess.init_features(rustc_expand::config::features(sess, &pre_configured_attrs));
let qcx = passes::create_global_ctxt(
self.compiler,
crate_types,
stable_crate_id,
lint_store,
self.dep_graph(dep_graph_future),
untracked,
@ -320,7 +320,7 @@ impl<'tcx> Queries<'tcx> {
let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| {
(
if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None },
if tcx.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None },
tcx.output_filenames(()).clone(),
tcx.dep_graph.clone(),
)

View file

@ -103,12 +103,20 @@ fromRust(LLVMRustCounterExprKind Kind) {
}
extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer(
const char* const Filenames[],
const char *const Filenames[],
size_t FilenamesLen,
const size_t *const Lengths,
size_t LengthsLen,
RustStringRef BufferOut) {
if (FilenamesLen != LengthsLen) {
report_fatal_error(
"Mismatched lengths in LLVMRustCoverageWriteFilenamesSectionToBuffer");
}
SmallVector<std::string,32> FilenameRefs;
FilenameRefs.reserve(FilenamesLen);
for (size_t i = 0; i < FilenamesLen; i++) {
FilenameRefs.push_back(std::string(Filenames[i]));
FilenameRefs.emplace_back(Filenames[i], Lengths[i]);
}
auto FilenamesWriter =
coverage::CoverageFilenamesSectionWriter(ArrayRef<std::string>(FilenameRefs));
@ -153,8 +161,11 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
CoverageMappingWriter.write(OS);
}
extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) {
StringRef FuncNameRef(FuncName);
extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(
LLVMValueRef F,
const char *FuncName,
size_t FuncNameLen) {
StringRef FuncNameRef(FuncName, FuncNameLen);
return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef));
}

View file

@ -545,7 +545,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
name,
// The all loop is because `--crate-type=rlib --crate-type=rlib` is
// legal and produces both inside this type.
self.sess.crate_types().iter().all(|c| *c == CrateType::Rlib),
self.tcx.crate_types().iter().all(|c| *c == CrateType::Rlib),
hash,
extra_filename,
false, // is_host
@ -689,7 +689,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
// If we're only compiling an rlib, then there's no need to select a
// panic runtime, so we just skip this section entirely.
let any_non_rlib = self.sess.crate_types().iter().any(|ct| *ct != CrateType::Rlib);
let any_non_rlib = self.tcx.crate_types().iter().any(|ct| *ct != CrateType::Rlib);
if !any_non_rlib {
info!("panic runtime injection skipped, only generating rlib");
return;
@ -818,7 +818,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
// At this point we've determined that we need an allocator. Let's see
// if our compilation session actually needs an allocator based on what
// we're emitting.
let all_rlib = self.sess.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib));
let all_rlib = self.tcx.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib));
if all_rlib {
return;
}

View file

@ -66,8 +66,7 @@ use rustc_session::cstore::CrateDepKind;
use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic};
pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies {
tcx.sess
.crate_types()
tcx.crate_types()
.iter()
.map(|&ty| {
let linkage = calculate_type(tcx, ty);

View file

@ -56,7 +56,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
// Always create a file at `metadata_filename`, even if we have nothing to write to it.
// This simplifies the creation of the output `out_filename` when requested.
let metadata_kind = tcx.sess.metadata_kind();
let metadata_kind = tcx.metadata_kind();
match metadata_kind {
MetadataKind::None => {
std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {

View file

@ -52,10 +52,11 @@ fn find_bundled_library(
verbatim: Option<bool>,
kind: NativeLibKind,
has_cfg: bool,
sess: &Session,
tcx: TyCtxt<'_>,
) -> Option<Symbol> {
let sess = tcx.sess;
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
&& sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
&& tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
&& (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
{
let verbatim = verbatim.unwrap_or(false);
@ -364,7 +365,7 @@ impl<'tcx> Collector<'tcx> {
};
let kind = kind.unwrap_or(NativeLibKind::Unspecified);
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess);
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
self.libs.push(NativeLib {
name,
filename,
@ -442,9 +443,13 @@ impl<'tcx> Collector<'tcx> {
// Add if not found
let new_name: Option<&str> = passed_lib.new_name.as_deref();
let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
let sess = self.tcx.sess;
let filename =
find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess);
let filename = find_bundled_library(
name,
passed_lib.verbatim,
passed_lib.kind,
false,
self.tcx,
);
self.libs.push(NativeLib {
name,
filename,

View file

@ -1741,7 +1741,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
fn encode_proc_macros(&mut self) -> Option<ProcMacroData> {
let is_proc_macro = self.tcx.sess.crate_types().contains(&CrateType::ProcMacro);
let is_proc_macro = self.tcx.crate_types().contains(&CrateType::ProcMacro);
if is_proc_macro {
let tcx = self.tcx;
let hir = tcx.hir();
@ -2204,7 +2204,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
source_file_cache,
interpret_allocs: Default::default(),
required_source_files,
is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro),
is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro),
hygiene_ctxt: &hygiene_ctxt,
symbol_table: Default::default(),
};

View file

@ -60,7 +60,7 @@ use crate::mir::mono::MonoItem;
use crate::ty::TyCtxt;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::definitions::DefPathHash;
use rustc_hir::{HirId, ItemLocalId, OwnerId};
use rustc_query_system::dep_graph::FingerprintStyle;
@ -371,7 +371,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId {
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
if tcx.fingerprint_style(dep_node.kind) == FingerprintStyle::HirId {
let (local_hash, local_id) = Fingerprint::from(dep_node.hash).split();
let def_path_hash = DefPathHash::new(tcx.sess.local_stable_crate_id(), local_hash);
let def_path_hash = DefPathHash::new(tcx.stable_crate_id(LOCAL_CRATE), local_hash);
let def_id = tcx
.def_path_hash_to_def_id(def_path_hash, &mut || {
panic!("Failed to extract HirId: {:?} {}", dep_node.kind, dep_node.hash)

View file

@ -1210,7 +1210,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
owner_spans.hash_stable(&mut hcx, &mut stable_hasher);
}
tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher);
tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher);
// Hash visibility information since it does not appear in HIR.
resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher);
resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher);

View file

@ -72,6 +72,6 @@ pub fn metadata_symbol_name(tcx: TyCtxt<'_>) -> String {
format!(
"rust_metadata_{}_{:08x}",
tcx.crate_name(LOCAL_CRATE),
tcx.sess.local_stable_crate_id(),
tcx.stable_crate_id(LOCAL_CRATE),
)
}

View file

@ -524,13 +524,13 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> {
// local crate's ID. Otherwise there can be collisions between CGUs
// instantiating stuff for upstream crates.
let local_crate_id = if cnum != LOCAL_CRATE {
let local_stable_crate_id = tcx.sess.local_stable_crate_id();
let local_stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
format!("-in-{}.{:08x}", tcx.crate_name(LOCAL_CRATE), local_stable_crate_id)
} else {
String::new()
};
let stable_crate_id = tcx.sess.local_stable_crate_id();
let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
format!("{}.{:08x}{}", tcx.crate_name(cnum), stable_crate_id, local_crate_id)
});

View file

@ -59,8 +59,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use rustc_session::config::CrateType;
use rustc_session::cstore::{CrateStoreDyn, Untracked};
use rustc_session::lint::Lint;
use rustc_session::Limit;
use rustc_session::Session;
use rustc_session::{Limit, MetadataKind, Session};
use rustc_span::def_id::{DefPathHash, StableCrateId};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
@ -527,6 +526,13 @@ pub struct GlobalCtxt<'tcx> {
interners: CtxtInterners<'tcx>,
pub sess: &'tcx Session,
crate_types: Vec<CrateType>,
/// The `stable_crate_id` is constructed out of the crate name and all the
/// `-C metadata` arguments passed to the compiler. Its value forms a unique
/// global identifier for the crate. It is used to allow multiple crates
/// with the same name to coexist. See the
/// `rustc_symbol_mangling` crate for more information.
stable_crate_id: StableCrateId,
/// This only ever stores a `LintStore` but we don't want a dependency on that type here.
///
@ -687,6 +693,8 @@ impl<'tcx> TyCtxt<'tcx> {
/// has a valid reference to the context, to allow formatting values that need it.
pub fn create_global_ctxt(
s: &'tcx Session,
crate_types: Vec<CrateType>,
stable_crate_id: StableCrateId,
lint_store: Lrc<dyn Any + sync::DynSend + sync::DynSync>,
arena: &'tcx WorkerLocal<Arena<'tcx>>,
hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>,
@ -705,6 +713,8 @@ impl<'tcx> TyCtxt<'tcx> {
GlobalCtxt {
sess: s,
crate_types,
stable_crate_id,
lint_store,
arena,
hir_arena,
@ -800,10 +810,47 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
#[inline]
pub fn crate_types(self) -> &'tcx [CrateType] {
&self.crate_types
}
pub fn metadata_kind(self) -> MetadataKind {
self.crate_types()
.iter()
.map(|ty| match *ty {
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => {
MetadataKind::None
}
CrateType::Rlib => MetadataKind::Uncompressed,
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
})
.max()
.unwrap_or(MetadataKind::None)
}
pub fn needs_metadata(self) -> bool {
self.metadata_kind() != MetadataKind::None
}
pub fn needs_crate_hash(self) -> bool {
// Why is the crate hash needed for these configurations?
// - debug_assertions: for the "fingerprint the result" check in
// `rustc_query_system::query::plumbing::execute_job`.
// - incremental: for query lookups.
// - needs_metadata: for putting into crate metadata.
// - instrument_coverage: for putting into coverage data (see
// `hash_mir_source`).
cfg!(debug_assertions)
|| self.sess.opts.incremental.is_some()
|| self.needs_metadata()
|| self.sess.instrument_coverage()
}
#[inline]
pub fn stable_crate_id(self, crate_num: CrateNum) -> StableCrateId {
if crate_num == LOCAL_CRATE {
self.sess.local_stable_crate_id()
self.stable_crate_id
} else {
self.cstore_untracked().stable_crate_id(crate_num)
}
@ -813,7 +860,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// that the crate in question has already been loaded by the CrateStore.
#[inline]
pub fn stable_crate_id_to_crate_num(self, stable_crate_id: StableCrateId) -> CrateNum {
if stable_crate_id == self.sess.local_stable_crate_id() {
if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) {
LOCAL_CRATE
} else {
self.cstore_untracked().stable_crate_id_to_crate_num(stable_crate_id)
@ -830,7 +877,7 @@ impl<'tcx> TyCtxt<'tcx> {
// If this is a DefPathHash from the local crate, we can look up the
// DefId in the tcx's `Definitions`.
if stable_crate_id == self.sess.local_stable_crate_id() {
if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) {
self.untracked.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id()
} else {
// If this is a DefPathHash from an upstream crate, let the CrateStore map
@ -847,7 +894,7 @@ impl<'tcx> TyCtxt<'tcx> {
// statements within the query system and we'd run into endless
// recursion otherwise.
let (crate_name, stable_crate_id) = if def_id.is_local() {
(self.crate_name(LOCAL_CRATE), self.sess.local_stable_crate_id())
(self.crate_name(LOCAL_CRATE), self.stable_crate_id(LOCAL_CRATE))
} else {
let cstore = &*self.cstore_untracked();
(cstore.crate_name(def_id.krate), cstore.stable_crate_id(def_id.krate))
@ -986,7 +1033,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn local_crate_exports_generics(self) -> bool {
debug_assert!(self.sess.opts.share_generics());
self.sess.crate_types().iter().any(|crate_type| {
self.crate_types().iter().any(|crate_type| {
match crate_type {
CrateType::Executable
| CrateType::Staticlib

View file

@ -31,7 +31,7 @@ struct EntryContext<'tcx> {
}
fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable);
let any_exe = tcx.crate_types().iter().any(|ty| *ty == CrateType::Executable);
if !any_exe {
// No need to find a main function.
return None;

View file

@ -364,10 +364,10 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
let effective_visibilities = &tcx.effective_visibilities(());
let any_library =
tcx.sess.crate_types().iter().any(|ty| {
*ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro
});
let any_library = tcx
.crate_types()
.iter()
.any(|ty| *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro);
let mut reachable_context = ReachableContext {
tcx,
maybe_typeck_results: None,

View file

@ -43,7 +43,7 @@ pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems) {
fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) {
// We only need to check for the presence of weak lang items if we're
// emitting something that's not an rlib.
let needs_check = tcx.sess.crate_types().iter().any(|kind| match *kind {
let needs_check = tcx.crate_types().iter().any(|kind| match *kind {
CrateType::Dylib
| CrateType::ProcMacro
| CrateType::Cdylib

View file

@ -4374,7 +4374,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
if let Some(res) = res
&& let Some(def_id) = res.opt_def_id()
&& !def_id.is_local()
&& self.r.tcx.sess.crate_types().contains(&CrateType::ProcMacro)
&& self.r.tcx.crate_types().contains(&CrateType::ProcMacro)
&& matches!(self.r.tcx.sess.opts.resolve_doc_links, ResolveDocLinks::ExportedMetadata) {
// Encoding foreign def ids in proc macro crate metadata will ICE.
return None;
@ -4389,7 +4389,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
match self.r.tcx.sess.opts.resolve_doc_links {
ResolveDocLinks::None => return,
ResolveDocLinks::ExportedMetadata
if !self.r.tcx.sess.crate_types().iter().copied().any(CrateType::has_metadata)
if !self.r.tcx.crate_types().iter().copied().any(CrateType::has_metadata)
|| !maybe_exported.eval(self.r) =>
{
return;
@ -4448,7 +4448,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
.into_iter()
.filter_map(|tr| {
if !tr.def_id.is_local()
&& self.r.tcx.sess.crate_types().contains(&CrateType::ProcMacro)
&& self.r.tcx.crate_types().contains(&CrateType::ProcMacro)
&& matches!(
self.r.tcx.sess.opts.resolve_doc_links,
ResolveDocLinks::ExportedMetadata

View file

@ -152,14 +152,6 @@ pub struct Session {
/// Input, input file path and output file path to this compilation process.
pub io: CompilerIO,
crate_types: OnceCell<Vec<CrateType>>,
/// The `stable_crate_id` is constructed out of the crate name and all the
/// `-C metadata` arguments passed to the compiler. Its value forms a unique
/// global identifier for the crate. It is used to allow multiple crates
/// with the same name to coexist. See the
/// `rustc_symbol_mangling` crate for more information.
pub stable_crate_id: OnceCell<StableCrateId>,
features: OnceCell<rustc_feature::Features>,
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
@ -310,55 +302,11 @@ impl Session {
self.parse_sess.span_diagnostic.emit_future_breakage_report(diags);
}
pub fn local_stable_crate_id(&self) -> StableCrateId {
self.stable_crate_id.get().copied().unwrap()
}
pub fn crate_types(&self) -> &[CrateType] {
self.crate_types.get().unwrap().as_slice()
}
/// Returns true if the crate is a testing one.
pub fn is_test_crate(&self) -> bool {
self.opts.test
}
pub fn needs_crate_hash(&self) -> bool {
// Why is the crate hash needed for these configurations?
// - debug_assertions: for the "fingerprint the result" check in
// `rustc_query_system::query::plumbing::execute_job`.
// - incremental: for query lookups.
// - needs_metadata: for putting into crate metadata.
// - instrument_coverage: for putting into coverage data (see
// `hash_mir_source`).
cfg!(debug_assertions)
|| self.opts.incremental.is_some()
|| self.needs_metadata()
|| self.instrument_coverage()
}
pub fn metadata_kind(&self) -> MetadataKind {
self.crate_types()
.iter()
.map(|ty| match *ty {
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => {
MetadataKind::None
}
CrateType::Rlib => MetadataKind::Uncompressed,
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
})
.max()
.unwrap_or(MetadataKind::None)
}
pub fn needs_metadata(&self) -> bool {
self.metadata_kind() != MetadataKind::None
}
pub fn init_crate_types(&self, crate_types: Vec<CrateType>) {
self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
}
#[rustc_lint_diagnostics]
#[track_caller]
pub fn struct_span_warn<S: Into<MultiSpan>>(
@ -1516,8 +1464,6 @@ pub fn build_session(
parse_sess,
sysroot,
io,
crate_types: OnceCell::new(),
stable_crate_id: OnceCell::new(),
features: OnceCell::new(),
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
cgu_reuse_tracker,

View file

@ -63,6 +63,10 @@ pub fn trait_def(did: DefId) -> stable_mir::ty::TraitDef {
with_tables(|t| t.trait_def(did))
}
pub fn impl_def(did: DefId) -> stable_mir::ty::ImplDef {
with_tables(|t| t.impl_def(did))
}
impl<'tcx> Tables<'tcx> {
pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId {
self.def_ids[item.0]
@ -72,6 +76,10 @@ impl<'tcx> Tables<'tcx> {
self.def_ids[trait_def.0]
}
pub fn impl_trait_def_id(&self, impl_def: &stable_mir::ty::ImplDef) -> DefId {
self.def_ids[impl_def.0]
}
pub fn crate_item(&mut self, did: DefId) -> stable_mir::CrateItem {
stable_mir::CrateItem(self.create_def_id(did))
}
@ -116,6 +124,10 @@ impl<'tcx> Tables<'tcx> {
stable_mir::ty::ConstDef(self.create_def_id(did))
}
pub fn impl_def(&mut self, did: DefId) -> stable_mir::ty::ImplDef {
stable_mir::ty::ImplDef(self.create_def_id(did))
}
fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
// FIXME: this becomes inefficient when we have too many ids
for (i, &d) in self.def_ids.iter().enumerate() {

View file

@ -59,6 +59,20 @@ impl<'tcx> Context for Tables<'tcx> {
trait_def.stable(self)
}
fn all_trait_impls(&mut self) -> stable_mir::ImplTraitDecls {
self.tcx
.trait_impls_in_crate(LOCAL_CRATE)
.iter()
.map(|impl_def_id| self.impl_def(*impl_def_id))
.collect()
}
fn trait_impl(&mut self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait {
let def_id = self.impl_trait_def_id(impl_def);
let impl_trait = self.tcx.impl_trait_ref(def_id).unwrap();
impl_trait.stable(self)
}
fn mir_body(&mut self, item: &stable_mir::CrateItem) -> stable_mir::mir::Body {
let def_id = self.item_def_id(item);
let mir = self.tcx.optimized_mir(def_id);
@ -840,6 +854,19 @@ where
}
}
impl<'tcx, S, V> Stable<'tcx> for ty::EarlyBinder<S>
where
S: Stable<'tcx, T = V>,
{
type T = stable_mir::ty::EarlyBinder<V>;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
use stable_mir::ty::EarlyBinder;
EarlyBinder { value: self.as_ref().skip_binder().stable(tables) }
}
}
impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
type T = stable_mir::ty::FnSig;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
@ -1154,3 +1181,12 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::ConstantKind<'tcx> {
}
}
}
impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> {
type T = stable_mir::ty::TraitRef;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
use stable_mir::ty::TraitRef;
TraitRef { def_id: rustc_internal::trait_def(self.def_id), args: self.args.stable(tables) }
}
}

View file

@ -15,7 +15,7 @@ use std::cell::Cell;
use crate::rustc_smir::Tables;
use self::ty::{TraitDecl, TraitDef, Ty, TyKind};
use self::ty::{ImplDef, ImplTrait, TraitDecl, TraitDef, Ty, TyKind};
pub mod mir;
pub mod ty;
@ -32,9 +32,12 @@ pub type DefId = usize;
/// A list of crate items.
pub type CrateItems = Vec<CrateItem>;
/// A list of crate items.
/// A list of trait decls.
pub type TraitDecls = Vec<TraitDef>;
/// A list of impl trait decls.
pub type ImplTraitDecls = Vec<ImplDef>;
/// Holds information about a crate.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Crate {
@ -89,6 +92,8 @@ pub trait Context {
fn mir_body(&mut self, item: &CrateItem) -> mir::Body;
fn all_trait_decls(&mut self) -> TraitDecls;
fn trait_decl(&mut self, trait_def: &TraitDef) -> TraitDecl;
fn all_trait_impls(&mut self) -> ImplTraitDecls;
fn trait_impl(&mut self, trait_impl: &ImplDef) -> ImplTrait;
/// Get information about the local crate.
fn local_crate(&self) -> Crate;
/// Retrieve a list of all external crates.

View file

@ -119,6 +119,15 @@ impl TraitDef {
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ImplDef(pub(crate) DefId);
impl ImplDef {
pub fn trait_impl(&self) -> ImplTrait {
with(|cx| cx.trait_impl(self))
}
}
#[derive(Clone, Debug)]
pub struct GenericArgs(pub Vec<GenericArgKind>);
@ -196,6 +205,11 @@ pub struct Binder<T> {
pub bound_vars: Vec<BoundVariableKind>,
}
#[derive(Clone, Debug)]
pub struct EarlyBinder<T> {
pub value: T,
}
#[derive(Clone, Debug)]
pub enum BoundVariableKind {
Ty(BoundTyKind),
@ -432,3 +446,10 @@ pub struct TraitDecl {
pub implement_via_object: bool,
pub deny_explicit_impl: bool,
}
pub type ImplTrait = EarlyBinder<TraitRef>;
pub struct TraitRef {
pub def_id: TraitDef,
pub args: GenericArgs,
}

View file

@ -180,7 +180,7 @@ fn compute_symbol_name<'tcx>(
if let Some(def_id) = def_id.as_local() {
if tcx.proc_macro_decls_static(()) == Some(def_id) {
let stable_crate_id = tcx.sess.local_stable_crate_id();
let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
}
}

View file

@ -824,11 +824,8 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
}
ty::Array(ty0, len) => {
let len = len
.try_to_scalar()
.unwrap()
.to_u64()
.unwrap_or_else(|_| panic!("failed to convert length to u64"));
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len);
}

View file

@ -19,21 +19,25 @@ rustc_index::newtype_index! {
#[derive(Debug, Clone)]
pub(super) struct ProvisionalEntry<'tcx> {
// In case we have a coinductive cycle, this is the
// the currently least restrictive result of this goal.
pub(super) response: QueryResult<'tcx>,
// In case of a cycle, the position of deepest stack entry involved
// in that cycle. This is monotonically decreasing in the stack as all
// elements between the current stack element in the deepest stack entry
// involved have to also be involved in that cycle.
//
// We can only move entries to the global cache once we're complete done
// with the cycle. If this entry has not been involved in a cycle,
// this is just its own depth.
/// In case we have a coinductive cycle, this is the
/// the current provisional result of this goal.
///
/// This starts out as `None` for all goals and gets to some
/// when the goal gets popped from the stack or we rerun evaluation
/// for this goal to reach a fixpoint.
pub(super) response: Option<QueryResult<'tcx>>,
/// In case of a cycle, the position of deepest stack entry involved
/// in that cycle. This is monotonically decreasing in the stack as all
/// elements between the current stack element in the deepest stack entry
/// involved have to also be involved in that cycle.
///
/// We can only move entries to the global cache once we're complete done
/// with the cycle. If this entry has not been involved in a cycle,
/// this is just its own depth.
pub(super) depth: StackDepth,
// The goal for this entry. Should always be equal to the corresponding goal
// in the lookup table.
/// The goal for this entry. Should always be equal to the corresponding goal
/// in the lookup table.
pub(super) input: CanonicalInput<'tcx>,
}
@ -92,7 +96,7 @@ impl<'tcx> ProvisionalCache<'tcx> {
self.entries[entry_index].depth
}
pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> QueryResult<'tcx> {
pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> Option<QueryResult<'tcx>> {
self.entries[entry_index].response
}
}

View file

@ -13,7 +13,7 @@ use rustc_middle::traits::solve::CacheData;
use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
use rustc_middle::ty::TyCtxt;
use rustc_session::Limit;
use std::{collections::hash_map::Entry, mem};
use std::collections::hash_map::Entry;
rustc_index::newtype_index! {
pub struct StackDepth {}
@ -216,8 +216,8 @@ impl<'tcx> SearchGraph<'tcx> {
cycle_participants: Default::default(),
};
assert_eq!(self.stack.push(entry), depth);
let response = Self::response_no_constraints(tcx, input, Certainty::Yes);
let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input });
let entry_index =
cache.entries.push(ProvisionalEntry { response: None, depth, input });
v.insert(entry_index);
}
// We have a nested goal which relies on a goal `root` deeper in the stack.
@ -243,23 +243,31 @@ impl<'tcx> SearchGraph<'tcx> {
root.cycle_participants.insert(e.input);
}
// NOTE: The goals on the stack aren't the only goals involved in this cycle.
// We can also depend on goals which aren't part of the stack but coinductively
// depend on the stack themselves. We already checked whether all the goals
// between these goals and their root on the stack. This means that as long as
// each goal in a cycle is checked for coinductivity by itself, simply checking
// the stack is enough.
if self.stack.raw[stack_depth.index()..]
.iter()
.all(|g| g.input.value.goal.predicate.is_coinductive(tcx))
{
// If we're in a coinductive cycle, we have to retry proving the current goal
// until we reach a fixpoint.
self.stack[stack_depth].has_been_used = true;
return cache.provisional_result(entry_index);
// If we're in a cycle, we have to retry proving the current goal
// until we reach a fixpoint.
self.stack[stack_depth].has_been_used = true;
return if let Some(result) = cache.provisional_result(entry_index) {
result
} else {
return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
}
// If we don't have a provisional result yet, the goal has to
// still be on the stack.
let mut goal_on_stack = false;
let mut is_coinductive = true;
for entry in self.stack.raw[stack_depth.index()..]
.iter()
.skip_while(|entry| entry.input != input)
{
goal_on_stack = true;
is_coinductive &= entry.input.value.goal.predicate.is_coinductive(tcx);
}
debug_assert!(goal_on_stack);
if is_coinductive {
Self::response_no_constraints(tcx, input, Certainty::Yes)
} else {
Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)
}
};
}
}
@ -288,15 +296,18 @@ impl<'tcx> SearchGraph<'tcx> {
let provisional_entry_index =
*cache.lookup_table.get(&stack_entry.input).unwrap();
let provisional_entry = &mut cache.entries[provisional_entry_index];
let prev_response = mem::replace(&mut provisional_entry.response, response);
if stack_entry.has_been_used && prev_response != response {
// If so, remove all entries whose result depends on this goal
// from the provisional cache...
if stack_entry.has_been_used
&& provisional_entry.response.map_or(true, |r| r != response)
{
// If so, update the provisional result for this goal and remove
// all entries whose result depends on this goal from the provisional
// cache...
//
// That's not completely correct, as a nested goal can also
// That's not completely correct, as a nested goal can also only
// depend on a goal which is lower in the stack so it doesn't
// actually depend on the current goal. This should be fairly
// rare and is hopefully not relevant for performance.
provisional_entry.response = Some(response);
#[allow(rustc::potential_query_instability)]
cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
cache.entries.truncate(provisional_entry_index.index() + 1);
@ -315,8 +326,8 @@ impl<'tcx> SearchGraph<'tcx> {
});
// We're now done with this goal. In case this goal is involved in a larger cycle
// do not remove it from the provisional cache and do not add it to the global
// cache.
// do not remove it from the provisional cache and update its provisional result.
// We only add the root of cycles to the global cache.
//
// It is not possible for any nested goal to depend on something deeper on the
// stack, as this would have also updated the depth of the current goal.
@ -348,6 +359,8 @@ impl<'tcx> SearchGraph<'tcx> {
dep_node,
result,
)
} else {
provisional_entry.response = Some(result);
}
result

View file

@ -2394,7 +2394,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
Ok(args) => args,
Err(()) => {
// FIXME: A rematch may fail when a candidate cache hit occurs
// on thefreshened form of the trait predicate, but the match
// on the freshened form of the trait predicate, but the match
// fails for some reason that is not captured in the freshened
// cache key. For example, equating an impl trait ref against
// the placeholder trait ref may fail due the Generalizer relation

View file

@ -200,7 +200,9 @@ pub enum TyKind<I: Interner> {
/// A tuple type. For example, `(i32, bool)`.
Tuple(I::ListTy),
/// A projection or opaque type. Both of these types
/// A projection, opaque type, weak type alias, or inherent associated type.
/// All of these types are represented as pairs of def-id and args, and can
/// be normalized, so they are grouped conceptually.
Alias(AliasKind, I::AliasTy),
/// A type parameter; for example, `T` in `fn f<T>(x: T) {}`.

View file

@ -18,7 +18,7 @@ use core::hash::{Hash, Hasher};
use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::mem;
use core::ptr::{NonNull, Unique};
use core::ptr::NonNull;
use super::SpecExtend;
use crate::alloc::{Allocator, Global};
@ -168,15 +168,16 @@ impl<T, A: Allocator> LinkedList<T, A> {
/// Adds the given node to the front of the list.
///
/// # Safety
/// `node` must point to a valid node that was boxed using the list's allocator.
/// `node` must point to a valid node that was boxed and leaked using the list's allocator.
/// This method takes ownership of the node, so the pointer should not be used again.
#[inline]
unsafe fn push_front_node(&mut self, node: Unique<Node<T>>) {
unsafe fn push_front_node(&mut self, node: NonNull<Node<T>>) {
// This method takes care not to create mutable references to whole nodes,
// to maintain validity of aliasing pointers into `element`.
unsafe {
(*node.as_ptr()).next = self.head;
(*node.as_ptr()).prev = None;
let node = Some(NonNull::from(node));
let node = Some(node);
match self.head {
None => self.tail = node,
@ -212,15 +213,16 @@ impl<T, A: Allocator> LinkedList<T, A> {
/// Adds the given node to the back of the list.
///
/// # Safety
/// `node` must point to a valid node that was boxed using the list's allocator.
/// `node` must point to a valid node that was boxed and leaked using the list's allocator.
/// This method takes ownership of the node, so the pointer should not be used again.
#[inline]
unsafe fn push_back_node(&mut self, node: Unique<Node<T>>) {
unsafe fn push_back_node(&mut self, node: NonNull<Node<T>>) {
// This method takes care not to create mutable references to whole nodes,
// to maintain validity of aliasing pointers into `element`.
unsafe {
(*node.as_ptr()).next = None;
(*node.as_ptr()).prev = self.tail;
let node = Some(NonNull::from(node));
let node = Some(node);
match self.tail {
None => self.head = node,
@ -842,8 +844,8 @@ impl<T, A: Allocator> LinkedList<T, A> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn push_front(&mut self, elt: T) {
let node = Box::new_in(Node::new(elt), &self.alloc);
let node_ptr = Unique::from(Box::leak(node));
// SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc
let node_ptr = NonNull::from(Box::leak(node));
// SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked
unsafe {
self.push_front_node(node_ptr);
}
@ -890,8 +892,8 @@ impl<T, A: Allocator> LinkedList<T, A> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn push_back(&mut self, elt: T) {
let node = Box::new_in(Node::new(elt), &self.alloc);
let node_ptr = Unique::from(Box::leak(node));
// SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc
let node_ptr = NonNull::from(Box::leak(node));
// SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked
unsafe {
self.push_back_node(node_ptr);
}

View file

@ -2491,9 +2491,9 @@ impl<T, A: Allocator> From<Vec<T, A>> for Rc<[T], A> {
///
/// ```
/// # use std::rc::Rc;
/// let original: Box<Vec<i32>> = Box::new(vec![1, 2, 3]);
/// let shared: Rc<Vec<i32>> = Rc::from(original);
/// assert_eq!(vec![1, 2, 3], *shared);
/// let unique: Vec<i32> = vec![1, 2, 3];
/// let shared: Rc<[i32]> = Rc::from(unique);
/// assert_eq!(&[1, 2, 3], &shared[..]);
/// ```
#[inline]
fn from(v: Vec<T, A>) -> Rc<[T], A> {

View file

@ -1,7 +1,7 @@
use super::*;
use crate::cmp::Ordering::{self, Equal, Greater, Less};
use crate::intrinsics::{self, const_eval_select};
use crate::mem;
use crate::mem::{self, SizedTypeProperties};
use crate::slice::{self, SliceIndex};
impl<T: ?Sized> *const T {
@ -995,14 +995,23 @@ impl<T: ?Sized> *const T {
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
// We could always go back to wrapping if unchecked becomes unacceptable
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub const unsafe fn sub(self, count: usize) -> Self
where
T: Sized,
{
// SAFETY: the caller must uphold the safety contract for `offset`.
unsafe { self.offset((count as isize).wrapping_neg()) }
if T::IS_ZST {
// Pointer arithmetic does nothing when the pointee is a ZST.
self
} else {
// SAFETY: the caller must uphold the safety contract for `offset`.
// Because the pointee is *not* a ZST, that means that `count` is
// at most `isize::MAX`, and thus the negation cannot overflow.
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
}
}
/// Calculates the offset from a pointer in bytes (convenience for

View file

@ -1,6 +1,7 @@
use super::*;
use crate::cmp::Ordering::{self, Equal, Greater, Less};
use crate::intrinsics::{self, const_eval_select};
use crate::mem::SizedTypeProperties;
use crate::slice::{self, SliceIndex};
impl<T: ?Sized> *mut T {
@ -1095,14 +1096,23 @@ impl<T: ?Sized> *mut T {
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
// We could always go back to wrapping if unchecked becomes unacceptable
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub const unsafe fn sub(self, count: usize) -> Self
where
T: Sized,
{
// SAFETY: the caller must uphold the safety contract for `offset`.
unsafe { self.offset((count as isize).wrapping_neg()) }
if T::IS_ZST {
// Pointer arithmetic does nothing when the pointee is a ZST.
self
} else {
// SAFETY: the caller must uphold the safety contract for `offset`.
// Because the pointee is *not* a ZST, that means that `count` is
// at most `isize::MAX`, and thus the negation cannot overflow.
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
}
}
/// Calculates the offset from a pointer in bytes (convenience for

View file

@ -25,14 +25,12 @@ hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep
std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] }
# Dependencies of the `backtrace` crate
addr2line = { version = "0.20.0", optional = true, default-features = false }
rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] }
miniz_oxide = { version = "0.7.0", optional = true, default-features = false, public = false }
[dependencies.object]
version = "0.31.1"
optional = true
default-features = false
features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive']
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
addr2line = { version = "0.20.0", optional = true, default-features = false }
object = { version = "0.31.1", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }

View file

@ -791,6 +791,7 @@ impl Write for &File {
self.inner.is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
@ -836,6 +837,7 @@ impl Write for File {
fn is_write_vectored(&self) -> bool {
(&&*self).is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(&*self).flush()
}
@ -881,6 +883,7 @@ impl Write for Arc<File> {
fn is_write_vectored(&self) -> bool {
(&**self).is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(&**self).flush()
}

View file

@ -310,6 +310,7 @@ impl<'a> Write for BorrowedCursor<'a> {
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> Result<()> {
Ok(())
}

View file

@ -397,9 +397,15 @@ extern crate libc;
#[allow(unused_extern_crates)]
extern crate unwind;
// FIXME: #94122 this extern crate definition only exist here to stop
// miniz_oxide docs leaking into std docs. Find better way to do it.
// Remove exclusion from tidy platform check when this removed.
#[doc(masked)]
#[allow(unused_extern_crates)]
#[cfg(feature = "miniz_oxide")]
#[cfg(all(
not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))),
feature = "miniz_oxide"
))]
extern crate miniz_oxide;
// During testing, this crate is not actually the "real" std library, but rather

View file

@ -647,6 +647,7 @@ impl Write for TcpStream {
self.0.is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
@ -685,6 +686,7 @@ impl Write for &TcpStream {
self.0.is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}

View file

@ -712,6 +712,7 @@ impl<'a> io::Write for &'a UnixStream {
self.0.is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}

View file

@ -280,6 +280,7 @@ impl Write for ChildStdin {
io::Write::is_write_vectored(&&*self)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(&*self).flush()
}
@ -299,6 +300,7 @@ impl Write for &ChildStdin {
self.inner.is_write_vectored()
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}

View file

@ -130,11 +130,8 @@ impl Barrier {
let local_gen = lock.generation_id;
lock.count += 1;
if lock.count < self.num_threads {
// We need a while loop to guard against spurious wakeups.
// https://en.wikipedia.org/wiki/Spurious_wakeup
while local_gen == lock.generation_id {
lock = self.cvar.wait(lock).unwrap();
}
let _guard =
self.cvar.wait_while(lock, |state| local_gen == state.generation_id).unwrap();
BarrierWaitResult(false)
} else {
lock.count = 0;

View file

@ -335,6 +335,7 @@ impl File {
false
}
#[inline]
pub fn flush(&self) -> io::Result<()> {
Ok(())
}

View file

@ -1204,6 +1204,7 @@ impl File {
self.0.write_vectored_at(bufs, offset)
}
#[inline]
pub fn flush(&self) -> io::Result<()> {
Ok(())
}

View file

@ -54,6 +54,7 @@ impl io::Write for Stdout {
true
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
@ -81,6 +82,7 @@ impl io::Write for Stderr {
true
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}

View file

@ -1129,7 +1129,7 @@ fn needs_codegen_config(run: &RunConfig<'_>) -> bool {
needs_codegen_cfg
}
const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_";
pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_";
fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool {
if path.path.to_str().unwrap().contains(&CODEGEN_BACKEND_PREFIX) {

View file

@ -20,6 +20,7 @@ use std::str::FromStr;
use crate::cache::{Interned, INTERNER};
use crate::cc_detect::{ndk_compiler, Language};
use crate::channel::{self, GitInfo};
use crate::compile::CODEGEN_BACKEND_PREFIX;
pub use crate::flags::Subcommand;
use crate::flags::{Color, Flags, Warnings};
use crate::util::{exe, output, t};
@ -1443,8 +1444,21 @@ impl Config {
.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
if let Some(ref backends) = rust.codegen_backends {
config.rust_codegen_backends =
backends.iter().map(|s| INTERNER.intern_str(s)).collect();
let available_backends = vec!["llvm", "cranelift", "gcc"];
config.rust_codegen_backends = backends.iter().map(|s| {
if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) {
if available_backends.contains(&backend) {
panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'.");
} else {
println!("help: '{s}' for 'rust.codegen-backends' might fail. \
Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
In this case, it would be referred to as '{backend}'.");
}
}
INTERNER.intern_str(s)
}).collect();
}
config.rust_codegen_units = rust.codegen_units.map(threads_from_config);

View file

@ -1,4 +1,4 @@
# `profile-sample-use
# `profile-sample-use`
---

View file

@ -4890,12 +4890,14 @@ Released 2018-09-13
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
[`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
[`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
[`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
@ -5190,6 +5192,7 @@ Released 2018-09-13
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
[`redundant_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_comparisons
[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names

View file

@ -41,7 +41,7 @@ fn main() {
matches.get_one::<String>("type").map(String::as_str),
matches.get_flag("msrv"),
) {
Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
Err(e) => eprintln!("Unable to create lint: {e}"),
}
},

View file

@ -47,7 +47,7 @@ fn check_install_precondition(force_override: bool) -> bool {
}
} else {
match fs::create_dir(vs_dir_path) {
Ok(_) => {
Ok(()) => {
println!("info: created `{VSCODE_DIR}` directory for clippy");
},
Err(err) => {

View file

@ -1,9 +1,7 @@
use std::borrow::Cow;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
use rustc_lint::LateContext;
@ -16,33 +14,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
return;
}
if_chain! {
if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind;
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr));
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind();
if matches!((from_mutbl, to_mutbl),
(Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut));
if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind
&& let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr))
&& let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind()
&& let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind()
&& matches!((from_mutbl, to_mutbl),
(Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut))
// The `U` in `pointer::cast` have to be `Sized`
// as explained here: https://github.com/rust-lang/rust/issues/60602.
if to_pointee_ty.is_sized(cx.tcx, cx.param_env);
then {
let mut applicability = Applicability::MachineApplicable;
let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability);
let turbofish = match &cast_to_hir_ty.kind {
TyKind::Infer => Cow::Borrowed(""),
TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""),
_ => Cow::Owned(format!("::<{to_pointee_ty}>")),
};
span_lint_and_sugg(
cx,
PTR_AS_PTR,
expr.span,
"`as` casting between raw pointers without changing its mutability",
"try `pointer::cast`, a safer alternative",
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
applicability,
);
}
&& to_pointee_ty.is_sized(cx.tcx, cx.param_env)
{
let mut app = Applicability::MachineApplicable;
let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
let turbofish = match &cast_to_hir_ty.kind {
TyKind::Infer => String::new(),
TyKind::Ptr(mut_ty) => {
if matches!(mut_ty.ty.kind, TyKind::Infer) {
String::new()
} else {
format!(
"::<{}>",
snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app)
)
}
},
_ => return,
};
span_lint_and_sugg(
cx,
PTR_AS_PTR,
expr.span,
"`as` casting between raw pointers without changing its mutability",
"try `pointer::cast`, a safer alternative",
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
app,
);
}
}

View file

@ -203,6 +203,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::if_let_mutex::IF_LET_MUTEX_INFO,
crate::if_not_else::IF_NOT_ELSE_INFO,
crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,
crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO,
crate::implicit_hasher::IMPLICIT_HASHER_INFO,
crate::implicit_return::IMPLICIT_RETURN_INFO,
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
@ -517,6 +518,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::operators::FLOAT_CMP_CONST_INFO,
crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO,
crate::operators::IDENTITY_OP_INFO,
crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
crate::operators::INTEGER_DIVISION_INFO,
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
@ -525,6 +527,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::operators::NEEDLESS_BITWISE_BOOL_INFO,
crate::operators::OP_REF_INFO,
crate::operators::PTR_EQ_INFO,
crate::operators::REDUNDANT_COMPARISONS_INFO,
crate::operators::SELF_ASSIGNMENT_INFO,
crate::operators::VERBOSE_BIT_MASK_INFO,
crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO,

View file

@ -1,6 +1,4 @@
use clippy_utils::diagnostics::{
span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
};
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
use clippy_utils::{is_lint_allowed, match_def_path, paths};
use if_chain::if_chain;
@ -8,15 +6,14 @@ use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
use rustc_hir::{
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind,
UnsafeSource, Unsafety,
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource, Unsafety,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv,
ToPredicate, TraitPredicate, Ty, TyCtxt,
self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate, TraitPredicate, Ty,
TyCtxt,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
@ -207,10 +204,13 @@ declare_lint_pass!(Derive => [
impl<'tcx> LateLintPass<'tcx> for Derive {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), .. }) = item.kind {
if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref),
..
}) = item.kind
{
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let is_automatically_derived =
cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
@ -327,12 +327,7 @@ fn check_ord_partial_ord<'tcx>(
}
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
fn check_copy_clone<'tcx>(
cx: &LateContext<'tcx>,
item: &Item<'_>,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
) {
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
let clone_id = match cx.tcx.lang_items().clone_trait() {
Some(id) if trait_ref.trait_def_id() == Some(id) => id,
_ => return,
@ -350,9 +345,10 @@ fn check_copy_clone<'tcx>(
if !is_copy(cx, ty) {
if ty_subs.non_erasable_generics().next().is_some() {
let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
impls
.iter()
.any(|&id| matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
impls.iter().any(|&id| {
matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
if ty_adt.did() == adt.did())
})
});
if !has_copy_impl {
return;
@ -431,14 +427,7 @@ struct UnsafeVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
fn visit_fn(
&mut self,
kind: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>,
body_id: BodyId,
_: Span,
id: LocalDefId,
) {
fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) {
if self.has_unsafe {
return;
}
@ -474,12 +463,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
}
/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
fn check_partial_eq_without_eq<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
) {
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
if_chain! {
if let ty::Adt(adt, args) = ty.kind();
if cx.tcx.visibility(adt.did()).is_public();

View file

@ -716,10 +716,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = EmitterWriter::new(
Box::new(io::sink()),
fallback_bundle,
);
let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
let sess = ParseSess::with_span_handler(handler, sm);

View file

@ -10,8 +10,8 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPat
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{
self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind,
GenericArgsRef, ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

View file

@ -0,0 +1,52 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use hir::PatKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_` in patterns of type `()`.
///
/// ### Why is this bad?
/// Matching with `()` explicitly instead of `_` outlines
/// the fact that the pattern contains no data. Also it
/// would detect a type change that `_` would ignore.
///
/// ### Example
/// ```rust
/// match std::fs::create_dir("tmp-work-dir") {
/// Ok(_) => println!("Working directory created"),
/// Err(s) => eprintln!("Could not create directory: {s}"),
/// }
/// ```
/// Use instead:
/// ```rust
/// match std::fs::create_dir("tmp-work-dir") {
/// Ok(()) => println!("Working directory created"),
/// Err(s) => eprintln!("Could not create directory: {s}"),
/// }
/// ```
#[clippy::version = "1.73.0"]
pub IGNORED_UNIT_PATTERNS,
pedantic,
"suggest replacing `_` by `()` in patterns where appropriate"
}
declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() {
span_lint_and_sugg(
cx,
IGNORED_UNIT_PATTERNS,
pat.span,
"matching over `()` is more explicit",
"use `()` instead of `_`",
String::from("()"),
Applicability::MachineApplicable,
);
}
}
}

View file

@ -147,6 +147,7 @@ mod future_not_send;
mod if_let_mutex;
mod if_not_else;
mod if_then_some_else_none;
mod ignored_unit_patterns;
mod implicit_hasher;
mod implicit_return;
mod implicit_saturating_add;
@ -1093,6 +1094,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
})
});
store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -162,7 +162,9 @@ fn never_loop_expr<'tcx>(
ExprKind::Binary(_, e1, e2)
| ExprKind::Assign(e1, e2, _)
| ExprKind::AssignOp(_, e1, e2)
| ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
| ExprKind::Index(e1, e2, _) => {
never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id)
},
ExprKind::Loop(b, _, _, _) => {
// Break can come from the inner loop so remove them.
absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))

View file

@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if !in_external_macro(cx.sess(), expr.span)
&& (
matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
|| cx.tcx.features().active(sym!(const_float_classify))
) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
&& let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind

View file

@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::path_to_local;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::visitors::{for_each_expr, is_local_used};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
@ -160,6 +161,11 @@ fn emit_redundant_guards<'tcx>(
}
/// Checks if the given `Expr` can also be represented as a `Pat`.
///
/// All literals generally also work as patterns, however float literals are special.
/// They are currently (as of 2023/08/08) still allowed in patterns, but that will become
/// an error in the future, and rustc already actively warns against this (see rust#41620),
/// so we don't consider those as usable within patterns for linting purposes.
fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
for_each_expr(expr, |expr| {
if match expr.kind {
@ -177,8 +183,8 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
ExprKind::AddrOf(..)
| ExprKind::Array(..)
| ExprKind::Tup(..)
| ExprKind::Struct(..)
| ExprKind::Lit(..) => true,
| ExprKind::Struct(..) => true,
ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
_ => false,
} {
return ControlFlow::Continue(());

View file

@ -1,44 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
use super::EXPECT_USED;
/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
recv: &hir::Expr<'_>,
is_err: bool,
allow_expect_in_tests: bool,
) {
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((EXPECT_USED, "an `Option`", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
} else {
None
};
let method = if is_err { "expect_err" } else { "expect" };
if allow_expect_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
return;
}
if let Some((lint, kind, none_value, none_prefix)) = mess {
span_lint_and_help(
cx,
lint,
expr.span,
&format!("used `{method}()` on {kind} value"),
None,
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
);
}
}

View file

@ -1,3 +1,4 @@
use super::FILTER_MAP_BOOL_THEN;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths::BOOL_THEN;
use clippy_utils::source::snippet_opt;
@ -7,10 +8,9 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::Binder;
use rustc_span::{sym, Span};
use super::FILTER_MAP_BOOL_THEN;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) {
if !in_external_macro(cx.sess(), expr.span)
&& is_trait_method(cx, expr, sym::Iterator)
@ -21,7 +21,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
// `inputs` and `params` here as we need both the type and the span
&& let param_ty = closure.fn_decl.inputs[0]
&& let param = body.params[0]
&& is_copy(cx, cx.typeck_results().node_type(param_ty.hir_id).peel_refs())
// Issue #11309
&& let param_ty = cx.tcx.liberate_late_bound_regions(
closure.def_id.to_def_id(),
Binder::bind_with_vars(
cx.typeck_results().node_type(param_ty.hir_id),
cx.tcx.late_bound_vars(cx.tcx.hir().local_def_id_to_hir_id(closure.def_id)),
),
)
&& is_copy(cx, param_ty)
&& let ExprKind::MethodCall(_, recv, [then_arg], _) = value.kind
&& let ExprKind::Closure(then_closure) = then_arg.kind
&& let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value)

View file

@ -17,7 +17,6 @@ mod collapsible_str_replace;
mod drain_collect;
mod err_expect;
mod expect_fun_call;
mod expect_used;
mod extend_with_drain;
mod filetype_is_file;
mod filter_map;
@ -105,7 +104,7 @@ mod unnecessary_lazy_eval;
mod unnecessary_literal_unwrap;
mod unnecessary_sort_by;
mod unnecessary_to_owned;
mod unwrap_used;
mod unwrap_expect_used;
mod useless_asref;
mod utils;
mod vec_resize_to_zero;
@ -3881,6 +3880,13 @@ impl Methods {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
}
},
("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
&& let body = cx.tcx.hir().body(arg.body)
&& let [param] = body.params
&& let Some(("chars", recv, _, _, _)) = method_call(recv) =>
{
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
}
("arg", [arg]) => {
suspicious_command_arg_space::check(cx, recv, arg, span);
}
@ -3941,13 +3947,27 @@ impl Methods {
match method_call(recv) {
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
_ => unwrap_expect_used::check(
cx,
expr,
recv,
false,
self.allow_expect_in_tests,
unwrap_expect_used::Variant::Expect,
),
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
("expect_err", [_]) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests);
unwrap_expect_used::check(
cx,
expr,
recv,
true,
self.allow_expect_in_tests,
unwrap_expect_used::Variant::Expect,
);
},
("extend", [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
@ -3999,20 +4019,9 @@ impl Methods {
unnecessary_join::check(cx, expr, recv, join_arg, span);
}
},
("skip", [arg]) => {
iter_skip_zero::check(cx, expr, arg);
if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
}
}
("last", []) => {
if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
},
("lock", []) => {
@ -4060,13 +4069,6 @@ impl Methods {
}
}
},
("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
&& let body = cx.tcx.hir().body(arg.body)
&& let [param] = body.params
&& let Some(("chars", recv, _, _, _)) = method_call(recv) =>
{
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
}
("nth", [n_arg]) => match method_call(recv) {
Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
@ -4120,6 +4122,13 @@ impl Methods {
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
}
},
("skip", [arg]) => {
iter_skip_zero::check(cx, expr, arg);
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
}
("sort", []) => {
stable_sort_primitive::check(cx, expr, recv);
},
@ -4142,10 +4151,8 @@ impl Methods {
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [_arg]) => {
if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
},
("take", []) => needless_option_take::check(cx, expr, recv),
@ -4180,11 +4187,25 @@ impl Methods {
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
unwrap_expect_used::check(
cx,
expr,
recv,
false,
self.allow_unwrap_in_tests,
unwrap_expect_used::Variant::Unwrap,
);
},
("unwrap_err", []) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests);
unwrap_expect_used::check(
cx,
expr,
recv,
true,
self.allow_unwrap_in_tests,
unwrap_expect_used::Variant::Unwrap,
);
},
("unwrap_or", [u_arg]) => {
match method_call(recv) {
@ -4214,6 +4235,9 @@ impl Methods {
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
("write", []) => {
readonly_write_lock::check(cx, expr, recv);
}
("zip", [arg]) => {
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
&& name.ident.name == sym::iter
@ -4221,9 +4245,6 @@ impl Methods {
range_zip_with_len::check(cx, expr, iter_recv, arg);
}
},
("write", []) => {
readonly_write_lock::check(cx, expr, recv);
}
_ => {},
}
}

View file

@ -0,0 +1,83 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
use rustc_hir::Expr;
use rustc_lint::{LateContext, Lint};
use rustc_middle::ty;
use rustc_span::sym;
use super::{EXPECT_USED, UNWRAP_USED};
#[derive(Clone, Copy, Eq, PartialEq)]
pub(super) enum Variant {
Unwrap,
Expect,
}
impl Variant {
fn method_name(self, is_err: bool) -> &'static str {
match (self, is_err) {
(Variant::Unwrap, true) => "unwrap_err",
(Variant::Unwrap, false) => "unwrap",
(Variant::Expect, true) => "expect_err",
(Variant::Expect, false) => "expect",
}
}
fn lint(self) -> &'static Lint {
match self {
Variant::Unwrap => UNWRAP_USED,
Variant::Expect => EXPECT_USED,
}
}
}
/// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their
/// `expect` counterparts).
pub(super) fn check(
cx: &LateContext<'_>,
expr: &Expr<'_>,
recv: &Expr<'_>,
is_err: bool,
allow_unwrap_in_tests: bool,
variant: Variant,
) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err {
("an `Option`", "None", "")
} else if is_type_diagnostic_item(cx, ty, sym::Result)
&& let ty::Adt(_, substs) = ty.kind()
&& let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type()
{
if is_never_like(t_or_e_ty) {
return;
}
("a `Result`", if is_err { "Ok" } else { "Err" }, "an ")
} else {
return;
};
let method_suffix = if is_err { "_err" } else { "" };
if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
return;
}
span_lint_and_then(
cx,
variant.lint(),
expr.span,
&format!("used `{}()` on {kind} value", variant.method_name(is_err)),
|diag| {
diag.note(format!("if this value is {none_prefix}`{none_value}`, it will panic"));
if variant == Variant::Unwrap && is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
diag.help(format!(
"consider using `expect{method_suffix}()` to provide a better panic message"
));
}
},
);
}

View file

@ -1,53 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
use super::{EXPECT_USED, UNWRAP_USED};
/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
recv: &hir::Expr<'_>,
is_err: bool,
allow_unwrap_in_tests: bool,
) {
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((UNWRAP_USED, "an `Option`", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
} else {
None
};
let method_suffix = if is_err { "_err" } else { "" };
if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
return;
}
if let Some((lint, kind, none_value, none_prefix)) = mess {
let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
format!(
"if you don't want to handle the `{none_value}` case gracefully, consider \
using `expect{method_suffix}()` to provide a better panic message"
)
} else {
format!("if this value is {none_prefix}`{none_value}`, it will panic")
};
span_lint_and_help(
cx,
lint,
expr.span,
&format!("used `unwrap{method_suffix}()` on {kind} value"),
None,
&help,
);
}
}

View file

@ -74,7 +74,6 @@ fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
use rustc_session::config::CrateType;
cx.tcx
.sess
.crate_types()
.iter()
.any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))

View file

@ -37,6 +37,11 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);
impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if e.span.from_expansion() {
// Issue #11268
return;
}
match e.kind {
ExprKind::Call(fn_expr, arguments) => {
if let ExprKind::Path(ref path) = fn_expr.kind {

View file

@ -0,0 +1,207 @@
#![allow(clippy::match_same_arms)]
use std::cmp::Ordering;
use clippy_utils::consts::{constant, Constant};
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{Ty, TypeckResults};
use rustc_span::source_map::{Span, Spanned};
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::source::snippet;
use clippy_utils::SpanlessEq;
use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
// Extract a comparison between a const and non-const
// Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42`
fn comparison_to_const<'tcx>(
cx: &LateContext<'tcx>,
typeck: &TypeckResults<'tcx>,
expr: &'tcx Expr<'tcx>,
) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> {
if_chain! {
if let ExprKind::Binary(operator, left, right) = expr.kind;
if let Ok(cmp_op) = CmpOp::try_from(operator.node);
then {
match (constant(cx, typeck, left), constant(cx, typeck, right)) {
(Some(_), Some(_)) => None,
(_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))),
(Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))),
_ => None,
}
} else {
None
}
}
}
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
and_op: Spanned<BinOpKind>,
left_cond: &'tcx Expr<'tcx>,
right_cond: &'tcx Expr<'tcx>,
span: Span,
) {
if_chain! {
// Ensure that the binary operator is &&
if and_op.node == BinOpKind::And;
// Check that both operands to '&&' are themselves a binary operation
// The `comparison_to_const` step also checks this, so this step is just an optimization
if let ExprKind::Binary(_, _, _) = left_cond.kind;
if let ExprKind::Binary(_, _, _) = right_cond.kind;
let typeck = cx.typeck_results();
// Check that both operands to '&&' compare a non-literal to a literal
if let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) =
comparison_to_const(cx, typeck, left_cond);
if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) =
comparison_to_const(cx, typeck, right_cond);
if left_type == right_type;
// Check that the same expression is compared in both comparisons
if SpanlessEq::new(cx).eq_expr(left_expr, right_expr);
if !left_expr.can_have_side_effects();
// Compare the two constant expressions
if let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const);
// Rule out the `x >= 42 && x <= 42` corner case immediately
// Mostly to simplify the implementation, but it is also covered by `clippy::double_comparisons`
if !matches!(
(&left_cmp_op, &right_cmp_op, ordering),
(CmpOp::Le | CmpOp::Ge, CmpOp::Le | CmpOp::Ge, Ordering::Equal)
);
then {
if left_cmp_op.direction() == right_cmp_op.direction() {
let lhs_str = snippet(cx, left_cond.span, "<lhs>");
let rhs_str = snippet(cx, right_cond.span, "<rhs>");
// We already know that either side of `&&` has no effect,
// but emit a different error message depending on which side it is
if left_side_is_useless(left_cmp_op, ordering) {
span_lint_and_note(
cx,
REDUNDANT_COMPARISONS,
span,
"left-hand side of `&&` operator has no effect",
Some(left_cond.span.until(right_cond.span)),
&format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"),
);
} else {
span_lint_and_note(
cx,
REDUNDANT_COMPARISONS,
span,
"right-hand side of `&&` operator has no effect",
Some(and_op.span.to(right_cond.span)),
&format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"),
);
}
// We could autofix this error but choose not to,
// because code triggering this lint probably not behaving correctly in the first place
}
else if !comparison_is_possible(left_cmp_op.direction(), ordering) {
let expr_str = snippet(cx, left_expr.span, "..");
let lhs_str = snippet(cx, left_const_expr.span, "<lhs>");
let rhs_str = snippet(cx, right_const_expr.span, "<rhs>");
let note = match ordering {
Ordering::Less => format!("since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"),
Ordering::Equal => format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`"),
Ordering::Greater => format!("since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"),
};
span_lint_and_note(
cx,
IMPOSSIBLE_COMPARISONS,
span,
"boolean expression will never evaluate to 'true'",
None,
&note,
);
};
}
}
}
fn left_side_is_useless(left_cmp_op: CmpOp, ordering: Ordering) -> bool {
// Special-case for equal constants with an inclusive comparison
if ordering == Ordering::Equal {
match left_cmp_op {
CmpOp::Lt | CmpOp::Gt => false,
CmpOp::Le | CmpOp::Ge => true,
}
} else {
match (left_cmp_op.direction(), ordering) {
(CmpOpDirection::Lesser, Ordering::Less) => false,
(CmpOpDirection::Lesser, Ordering::Equal) => false,
(CmpOpDirection::Lesser, Ordering::Greater) => true,
(CmpOpDirection::Greater, Ordering::Less) => true,
(CmpOpDirection::Greater, Ordering::Equal) => false,
(CmpOpDirection::Greater, Ordering::Greater) => false,
}
}
}
fn comparison_is_possible(left_cmp_direction: CmpOpDirection, ordering: Ordering) -> bool {
match (left_cmp_direction, ordering) {
(CmpOpDirection::Lesser, Ordering::Less | Ordering::Equal) => false,
(CmpOpDirection::Lesser, Ordering::Greater) => true,
(CmpOpDirection::Greater, Ordering::Greater | Ordering::Equal) => false,
(CmpOpDirection::Greater, Ordering::Less) => true,
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum CmpOpDirection {
Lesser,
Greater,
}
#[derive(Clone, Copy)]
enum CmpOp {
Lt,
Le,
Ge,
Gt,
}
impl CmpOp {
fn reverse(self) -> Self {
match self {
CmpOp::Lt => CmpOp::Gt,
CmpOp::Le => CmpOp::Ge,
CmpOp::Ge => CmpOp::Le,
CmpOp::Gt => CmpOp::Lt,
}
}
fn direction(self) -> CmpOpDirection {
match self {
CmpOp::Lt => CmpOpDirection::Lesser,
CmpOp::Le => CmpOpDirection::Lesser,
CmpOp::Ge => CmpOpDirection::Greater,
CmpOp::Gt => CmpOpDirection::Greater,
}
}
}
impl TryFrom<BinOpKind> for CmpOp {
type Error = ();
fn try_from(bin_op: BinOpKind) -> Result<Self, Self::Error> {
match bin_op {
BinOpKind::Lt => Ok(CmpOp::Lt),
BinOpKind::Le => Ok(CmpOp::Le),
BinOpKind::Ge => Ok(CmpOp::Ge),
BinOpKind::Gt => Ok(CmpOp::Gt),
_ => Err(()),
}
}
}

View file

@ -2,6 +2,7 @@ mod absurd_extreme_comparisons;
mod assign_op_pattern;
mod bit_mask;
mod cmp_owned;
mod const_comparisons;
mod double_comparison;
mod duration_subsec;
mod eq_op;
@ -298,6 +299,45 @@ declare_clippy_lint! {
"unnecessary double comparisons that can be simplified"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for double comparisons that can never succeed
///
/// ### Why is this bad?
/// The whole expression can be replaced by `false`,
/// which is probably not the programmer's intention
///
/// ### Example
/// ```rust
/// # let status_code = 200;
/// if status_code <= 400 && status_code > 500 {}
/// ```
#[clippy::version = "1.71.0"]
pub IMPOSSIBLE_COMPARISONS,
correctness,
"double comparisons that will never evaluate to `true`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for ineffective double comparisons against constants.
///
/// ### Why is this bad?
/// Only one of the comparisons has any effect on the result, the programmer
/// probably intended to flip one of the comparison operators, or compare a
/// different value entirely.
///
/// ### Example
/// ```rust
/// # let status_code = 200;
/// if status_code <= 400 && status_code < 500 {}
/// ```
#[clippy::version = "1.71.0"]
pub REDUNDANT_COMPARISONS,
correctness,
"double comparisons where one of them can be removed"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calculation of subsecond microseconds or milliseconds
@ -742,6 +782,8 @@ impl_lint_pass!(Operators => [
INEFFECTIVE_BIT_MASK,
VERBOSE_BIT_MASK,
DOUBLE_COMPARISONS,
IMPOSSIBLE_COMPARISONS,
REDUNDANT_COMPARISONS,
DURATION_SUBSEC,
EQ_OP,
OP_REF,
@ -786,6 +828,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
bit_mask::check(cx, e, op.node, lhs, rhs);
verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
double_comparison::check(cx, op.node, lhs, rhs, e.span);
const_comparisons::check(cx, op, lhs, rhs, e.span);
duration_subsec::check(cx, e, op.node, lhs, rhs);
float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
integer_division::check(cx, e, op.node, lhs, rhs);

View file

@ -155,7 +155,7 @@ fn try_get_option_occurrence<'tcx>(
});
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
match some_captures.get(local_id)
.or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
.or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id)))
{
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,

View file

@ -1,11 +1,13 @@
use crate::manual_let_else::{MatchLintBehaviour, MANUAL_LET_ELSE};
use crate::question_mark_used::QUESTION_MARK_USED;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::Msrv;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_path_lang_item, is_res_lang_ctor,
pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item,
is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks,
peel_blocks_with_stmt,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -299,13 +301,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) {
return;
}
if !in_constant(cx, stmt.hir_id) {
check_let_some_else_return_none(cx, stmt);
}
self.check_manual_let_else(cx, stmt);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !in_constant(cx, expr.hir_id) {
if !in_constant(cx, expr.hir_id) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) {
self.check_is_none_or_err_and_early_return(cx, expr);
self.check_if_let_some_or_err_and_early_return(cx, expr);
}

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_from_proc_macro;
use clippy_utils::ty::needs_ordered_drop;
use rustc_ast::Mutability;
use rustc_hir::def::Res;
use rustc_hir::{
BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath,
@ -9,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::{in_external_macro, is_from_async_await};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;
use rustc_span::DesugaringKind;
declare_clippy_lint! {
/// ### What it does
@ -47,6 +49,7 @@ declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]);
impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
if_chain! {
if !local.span.is_desugaring(DesugaringKind::Async);
// the pattern is a single by-value binding
if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind;
// the binding is not type-ascribed
@ -62,6 +65,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id);
// the previous binding has the same mutability
if find_binding(binding_pat, ident).unwrap().1 == mutability;
// the local does not change the effect of assignments to the binding. see #11290
if !affects_assignments(cx, mutability, binding_id, local.hir_id);
// the local does not affect the code's drop behavior
if !affects_drop_behavior(cx, binding_id, local.hir_id, expr);
// the local is user-controlled
@ -97,6 +102,14 @@ fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingAnnotation> {
ret
}
/// Check if a rebinding of a local changes the effect of assignments to the binding.
fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId, rebind: HirId) -> bool {
let hir = cx.tcx.hir();
// the binding is mutable and the rebinding is in a different scope than the original binding
mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
}
/// Check if a rebinding of a local affects the code's drop behavior.
fn affects_drop_behavior<'tcx>(
cx: &LateContext<'tcx>,

View file

@ -1,6 +1,8 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_lint_allowed;
use rustc_ast::LitKind;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -45,8 +47,8 @@ fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, f
return primty.name() == func_return_type_sym;
}
// type annotation is any other non generic type
if let hir::def::Res::Def(_, defid) = ty_resolved_path
// type annotation is a non generic type
if let hir::def::Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, defid) = ty_resolved_path
&& let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars()
{
return annotation_ty == func_return_type;
@ -130,8 +132,9 @@ fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> {
impl LateLintPass<'_> for RedundantTypeAnnotations {
fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) {
// type annotation part
if !local.span.from_expansion()
if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id)
// type annotation part
&& !local.span.from_expansion()
&& let Some(ty) = &local.ty
// initialization part

Some files were not shown because too many files have changed in this diff Show more