Merge commit 'd822110d3b5625b9dc80ccc442e06fc3cc851d76' into clippyup
This commit is contained in:
@@ -125,19 +125,19 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
|
||||
let mut unique_attr = None;
|
||||
pub fn get_unique_attr<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [ast::Attribute],
|
||||
name: &'static str,
|
||||
) -> Option<&'a ast::Attribute> {
|
||||
let mut unique_attr: Option<&ast::Attribute> = None;
|
||||
for attr in get_attr(sess, attrs, name) {
|
||||
match attr.style {
|
||||
ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
|
||||
ast::AttrStyle::Inner => {
|
||||
sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
|
||||
.span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
|
||||
.emit();
|
||||
},
|
||||
ast::AttrStyle::Outer => {
|
||||
sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
|
||||
},
|
||||
if let Some(duplicate) = unique_attr {
|
||||
sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
|
||||
.span_note(duplicate.span, "first definition found here")
|
||||
.emit();
|
||||
} else {
|
||||
unique_attr = Some(attr);
|
||||
}
|
||||
}
|
||||
unique_attr
|
||||
|
||||
@@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
|
||||
}
|
||||
}
|
||||
|
||||
fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
|
||||
cx.typeck_results()
|
||||
.expr_ty(e)
|
||||
.has_significant_drop(cx.tcx, cx.param_env)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
|
||||
struct V<'cx, 'tcx> {
|
||||
@@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||
},
|
||||
args,
|
||||
) => match self.cx.qpath_res(path, hir_id) {
|
||||
Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
|
||||
if self
|
||||
.cx
|
||||
.typeck_results()
|
||||
.expr_ty(e)
|
||||
.has_significant_drop(self.cx.tcx, self.cx.param_env)
|
||||
{
|
||||
res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
|
||||
if res_has_significant_drop(res, self.cx, e) {
|
||||
self.eagerness = ForceNoChange;
|
||||
return;
|
||||
}
|
||||
@@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||
self.eagerness |= NoChange;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(ref path) => {
|
||||
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
|
||||
self.eagerness = ForceNoChange;
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(name, ..) => {
|
||||
self.eagerness |= self
|
||||
.cx
|
||||
@@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Closure { .. }
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Path(_)
|
||||
| ExprKind::AddrOf(..)
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Repeat(..)
|
||||
|
||||
@@ -105,8 +105,6 @@ use rustc_middle::ty::{
|
||||
layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
|
||||
};
|
||||
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::sym;
|
||||
@@ -118,36 +116,17 @@ use crate::consts::{constant, Constant};
|
||||
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
|
||||
use crate::visitors::for_each_expr;
|
||||
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||
if let Ok(version) = RustcVersion::parse(msrv) {
|
||||
return Some(version);
|
||||
} else if let Some(sess) = sess {
|
||||
if let Some(span) = span {
|
||||
sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
|
||||
msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! extract_msrv_attr {
|
||||
($context:ident) => {
|
||||
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
|
||||
let sess = rustc_lint::LintContext::sess(cx);
|
||||
match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
|
||||
Some(msrv_attr) => {
|
||||
if let Some(msrv) = msrv_attr.value_str() {
|
||||
self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
|
||||
} else {
|
||||
sess.span_err(msrv_attr.span, "bad clippy attribute");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
self.msrv.enter_lint_attrs(sess, attrs);
|
||||
}
|
||||
|
||||
fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
|
||||
let sess = rustc_lint::LintContext::sess(cx);
|
||||
self.msrv.exit_lint_attrs(sess, attrs);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::attrs::get_unique_attr;
|
||||
|
||||
macro_rules! msrv_aliases {
|
||||
($($major:literal,$minor:literal,$patch:literal {
|
||||
@@ -40,3 +47,97 @@ msrv_aliases! {
|
||||
1,16,0 { STR_REPEAT }
|
||||
1,55,0 { SEEK_REWIND }
|
||||
}
|
||||
|
||||
fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||
if let Ok(version) = RustcVersion::parse(msrv) {
|
||||
return Some(version);
|
||||
} else if let Some(sess) = sess {
|
||||
if let Some(span) = span {
|
||||
sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Msrv {
|
||||
stack: Vec<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Msrv {
|
||||
fn new(initial: Option<RustcVersion>) -> Self {
|
||||
Self {
|
||||
stack: Vec::from_iter(initial),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
|
||||
let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
|
||||
.ok()
|
||||
.and_then(|v| parse_msrv(&v, None, None));
|
||||
let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(format!(
|
||||
"error reading Clippy's configuration file. `{s}` is not a valid Rust version"
|
||||
));
|
||||
None
|
||||
})
|
||||
});
|
||||
|
||||
// if both files have an msrv, let's compare them and emit a warning if they differ
|
||||
if let Some(cargo_msrv) = cargo_msrv
|
||||
&& let Some(clippy_msrv) = clippy_msrv
|
||||
&& clippy_msrv != cargo_msrv
|
||||
{
|
||||
sess.warn(format!(
|
||||
"the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
|
||||
));
|
||||
}
|
||||
|
||||
Self::new(clippy_msrv.or(cargo_msrv))
|
||||
}
|
||||
|
||||
/// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
|
||||
/// field in `Cargo.toml`
|
||||
///
|
||||
/// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
|
||||
/// `register_{late,early}_pass` callbacks
|
||||
pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
|
||||
static PARSED: OnceLock<Msrv> = OnceLock::new();
|
||||
|
||||
PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Option<RustcVersion> {
|
||||
self.stack.last().copied()
|
||||
}
|
||||
|
||||
pub fn meets(&self, required: RustcVersion) -> bool {
|
||||
self.current().map_or(true, |version| version.meets(required))
|
||||
}
|
||||
|
||||
fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
|
||||
if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
|
||||
if let Some(msrv) = msrv_attr.value_str() {
|
||||
return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
|
||||
}
|
||||
|
||||
sess.span_err(msrv_attr.span, "bad clippy attribute");
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
|
||||
if let Some(version) = Self::parse_attr(sess, attrs) {
|
||||
self.stack.push(version);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
|
||||
if Self::parse_attr(sess, attrs).is_some() {
|
||||
self.stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
|
||||
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
|
||||
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
|
||||
@@ -72,7 +74,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab
|
||||
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
|
||||
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
|
||||
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
|
||||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
|
||||
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
@@ -101,8 +102,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
|
||||
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
|
||||
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
|
||||
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
||||
// differ from the time of `rustc` even if the name stays the same.
|
||||
|
||||
use crate::msrvs::Msrv;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::{
|
||||
@@ -18,20 +19,22 @@ use std::borrow::Cow;
|
||||
|
||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||
|
||||
pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
|
||||
pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
|
||||
let def_id = body.source.def_id();
|
||||
let mut current = def_id;
|
||||
loop {
|
||||
let predicates = tcx.predicates_of(current);
|
||||
for (predicate, _) in predicates.predicates {
|
||||
match predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
|
||||
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
|
||||
ty::PredicateKind::Clause(
|
||||
ty::Clause::RegionOutlives(_)
|
||||
| ty::Clause::TypeOutlives(_)
|
||||
| ty::Clause::Projection(_)
|
||||
| ty::Clause::Trait(..),
|
||||
)
|
||||
| ty::PredicateKind::WellFormed(_)
|
||||
| ty::PredicateKind::Clause(ty::Clause::Projection(_))
|
||||
| ty::PredicateKind::ConstEvaluatable(..)
|
||||
| ty::PredicateKind::ConstEquate(..)
|
||||
| ty::PredicateKind::Clause(ty::Clause::Trait(..))
|
||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
|
||||
ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
|
||||
ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
|
||||
@@ -281,7 +284,7 @@ fn check_terminator<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> McfResult {
|
||||
let span = terminator.source_info.span;
|
||||
match &terminator.kind {
|
||||
@@ -365,7 +368,7 @@ fn check_terminator<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
|
||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||
tcx.is_const_fn(def_id)
|
||||
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
|
||||
if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
|
||||
@@ -384,15 +387,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bo
|
||||
|
||||
let since = rustc_span::Symbol::intern(short_version);
|
||||
|
||||
crate::meets_msrv(
|
||||
msrv,
|
||||
RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
|
||||
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
|
||||
}),
|
||||
)
|
||||
msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
|
||||
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
|
||||
}))
|
||||
} else {
|
||||
// Unstable const fn with the feature enabled.
|
||||
msrv.is_none()
|
||||
msrv.current().is_none()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene;
|
||||
use rustc_span::source_map::{original_sp, SourceMap};
|
||||
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
|
||||
@@ -204,11 +205,20 @@ pub fn snippet_with_applicability<'a, T: LintContext>(
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
snippet_with_applicability_sess(cx.sess(), span, default, applicability)
|
||||
}
|
||||
|
||||
fn snippet_with_applicability_sess<'a>(
|
||||
sess: &Session,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
if *applicability != Applicability::Unspecified && span.from_expansion() {
|
||||
*applicability = Applicability::MaybeIncorrect;
|
||||
}
|
||||
snippet_opt(cx, span).map_or_else(
|
||||
snippet_opt_sess(sess, span).map_or_else(
|
||||
|| {
|
||||
if *applicability == Applicability::MachineApplicable {
|
||||
*applicability = Applicability::HasPlaceholders;
|
||||
@@ -226,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau
|
||||
}
|
||||
|
||||
/// Converts a span to a code snippet. Returns `None` if not available.
|
||||
pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
cx.sess().source_map().span_to_snippet(span).ok()
|
||||
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
|
||||
snippet_opt_sess(cx.sess(), span)
|
||||
}
|
||||
|
||||
fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
|
||||
sess.source_map().span_to_snippet(span).ok()
|
||||
}
|
||||
|
||||
/// Converts a span (from a block) to a code snippet if available, otherwise use default.
|
||||
@@ -277,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>(
|
||||
|
||||
/// Same as `snippet_block`, but adapts the applicability level by the rules of
|
||||
/// `snippet_with_applicability`.
|
||||
pub fn snippet_block_with_applicability<'a, T: LintContext>(
|
||||
cx: &T,
|
||||
pub fn snippet_block_with_applicability<'a>(
|
||||
cx: &impl LintContext,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
@@ -299,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
|
||||
///
|
||||
/// This will also return whether or not the snippet is a macro call.
|
||||
pub fn snippet_with_context<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
cx: &impl LintContext,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> (Cow<'a, str>, bool) {
|
||||
snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
|
||||
}
|
||||
|
||||
fn snippet_with_context_sess<'a>(
|
||||
sess: &Session,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
@@ -318,7 +342,7 @@ pub fn snippet_with_context<'a>(
|
||||
);
|
||||
|
||||
(
|
||||
snippet_with_applicability(cx, span, default, applicability),
|
||||
snippet_with_applicability_sess(sess, span, default, applicability),
|
||||
is_macro_call,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -176,25 +176,28 @@ impl<'a> Sugg<'a> {
|
||||
}
|
||||
|
||||
/// Prepare a suggestion from an expression.
|
||||
pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
|
||||
pub fn ast(
|
||||
cx: &EarlyContext<'_>,
|
||||
expr: &ast::Expr,
|
||||
default: &'a str,
|
||||
ctxt: SyntaxContext,
|
||||
app: &mut Applicability,
|
||||
) -> Self {
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
|
||||
let snippet_without_expansion = |cx, span: Span, default| {
|
||||
if span.from_expansion() {
|
||||
snippet_with_macro_callsite(cx, span, default)
|
||||
} else {
|
||||
snippet(cx, span, default)
|
||||
}
|
||||
};
|
||||
|
||||
#[expect(clippy::match_wildcard_for_single_variants)]
|
||||
match expr.kind {
|
||||
_ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||
ast::ExprKind::AddrOf(..)
|
||||
| ast::ExprKind::Box(..)
|
||||
| ast::ExprKind::Closure { .. }
|
||||
| ast::ExprKind::If(..)
|
||||
| ast::ExprKind::Let(..)
|
||||
| ast::ExprKind::Unary(..)
|
||||
| ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
|
||||
| ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) {
|
||||
(snip, false) => Sugg::MaybeParen(snip),
|
||||
(snip, true) => Sugg::NonParen(snip),
|
||||
},
|
||||
ast::ExprKind::Async(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::Break(..)
|
||||
@@ -224,45 +227,49 @@ impl<'a> Sugg<'a> {
|
||||
| ast::ExprKind::Array(..)
|
||||
| ast::ExprKind::While(..)
|
||||
| ast::ExprKind::Await(..)
|
||||
| ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
|
||||
| ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
|
||||
AssocOp::DotDot,
|
||||
lhs.as_ref()
|
||||
.map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
|
||||
rhs.as_ref()
|
||||
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
|
||||
lhs.as_ref().map_or("".into(), |lhs| {
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||
}),
|
||||
rhs.as_ref().map_or("".into(), |rhs| {
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0
|
||||
}),
|
||||
),
|
||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
|
||||
AssocOp::DotDotEq,
|
||||
lhs.as_ref()
|
||||
.map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
|
||||
rhs.as_ref()
|
||||
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
|
||||
lhs.as_ref().map_or("".into(), |lhs| {
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||
}),
|
||||
rhs.as_ref().map_or("".into(), |rhs| {
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0
|
||||
}),
|
||||
),
|
||||
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
|
||||
AssocOp::Assign,
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, rhs.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||
astbinop2assignop(op),
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, rhs.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||
AssocOp::from_ast_binop(op.node),
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, rhs.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
|
||||
AssocOp::As,
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, ty.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
|
||||
AssocOp::Colon,
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, ty.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
|
||||
use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
|
||||
use rustc_infer::infer::{
|
||||
type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
|
||||
TyCtxtInferExt,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||
use rustc_middle::ty::{
|
||||
@@ -189,7 +192,13 @@ pub fn implements_trait<'tcx>(
|
||||
trait_id: DefId,
|
||||
ty_params: &[GenericArg<'tcx>],
|
||||
) -> bool {
|
||||
implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
|
||||
implements_trait_with_env(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
ty,
|
||||
trait_id,
|
||||
ty_params.iter().map(|&arg| Some(arg)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
|
||||
@@ -212,7 +221,11 @@ pub fn implements_trait_with_env<'tcx>(
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
|
||||
let ty_params = tcx.mk_substs(
|
||||
ty_params
|
||||
.into_iter()
|
||||
.map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
|
||||
);
|
||||
infcx
|
||||
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
|
||||
.must_apply_modulo_regions()
|
||||
@@ -712,7 +725,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
|
||||
}
|
||||
inputs = Some(i);
|
||||
},
|
||||
PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
|
||||
PredicateKind::Clause(ty::Clause::Projection(p))
|
||||
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
|
||||
{
|
||||
if output.is_some() {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
return None;
|
||||
@@ -992,14 +1007,12 @@ pub fn make_projection<'tcx>(
|
||||
|
||||
debug_assert!(
|
||||
generic_count == substs.len(),
|
||||
"wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
|
||||
"wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
|
||||
note: the expected parameters are: {:#?}\n\
|
||||
the given arguments are: `{:#?}`",
|
||||
the given arguments are: `{substs:#?}`",
|
||||
assoc_item.def_id,
|
||||
substs.len(),
|
||||
generic_count,
|
||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
||||
substs,
|
||||
);
|
||||
|
||||
if let Some((idx, (param, arg))) = params
|
||||
@@ -1017,14 +1030,11 @@ pub fn make_projection<'tcx>(
|
||||
{
|
||||
debug_assert!(
|
||||
false,
|
||||
"mismatched subst type at index {}: expected a {}, found `{:?}`\n\
|
||||
"mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
|
||||
note: the expected parameters are {:#?}\n\
|
||||
the given arguments are {:#?}",
|
||||
idx,
|
||||
the given arguments are {substs:#?}",
|
||||
param.descr(),
|
||||
arg,
|
||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
||||
substs,
|
||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,22 +170,22 @@ where
|
||||
cb: F,
|
||||
}
|
||||
|
||||
struct WithStmtGuarg<'a, F> {
|
||||
struct WithStmtGuard<'a, F> {
|
||||
val: &'a mut RetFinder<F>,
|
||||
prev_in_stmt: bool,
|
||||
}
|
||||
|
||||
impl<F> RetFinder<F> {
|
||||
fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
|
||||
fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
|
||||
let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
|
||||
WithStmtGuarg {
|
||||
WithStmtGuard {
|
||||
val: self,
|
||||
prev_in_stmt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
|
||||
impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
|
||||
type Target = RetFinder<F>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -193,13 +193,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
|
||||
impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for WithStmtGuarg<'_, F> {
|
||||
impl<F> Drop for WithStmtGuard<'_, F> {
|
||||
fn drop(&mut self) {
|
||||
self.val.in_stmt = self.prev_in_stmt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user