Auto merge of #12972 - Jarcho:span_ex, r=Manishearth
Add new span related utils The none-generic versions of the functions exist to help with both compile times and codegen. changelog: None
This commit is contained in:
commit
fe2fe7feed
@ -1,7 +1,7 @@
|
|||||||
//! calculate cognitive complexity and warn about overly complex functions
|
//! calculate cognitive complexity and warn about overly complex functions
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::{IntoSpan, SpanRangeExt};
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
|
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
|
||||||
@ -12,7 +12,7 @@
|
|||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::{sym, BytePos, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -50,7 +50,6 @@ pub fn new(limit: u64) -> Self {
|
|||||||
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
|
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
|
||||||
|
|
||||||
impl CognitiveComplexity {
|
impl CognitiveComplexity {
|
||||||
#[expect(clippy::cast_possible_truncation)]
|
|
||||||
fn check<'tcx>(
|
fn check<'tcx>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
@ -100,17 +99,12 @@ fn check<'tcx>(
|
|||||||
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
|
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
|
||||||
FnKind::Closure => {
|
FnKind::Closure => {
|
||||||
let header_span = body_span.with_hi(decl.output.span().lo());
|
let header_span = body_span.with_hi(decl.output.span().lo());
|
||||||
let pos = snippet_opt(cx, header_span).and_then(|snip| {
|
#[expect(clippy::range_plus_one)]
|
||||||
let low_offset = snip.find('|')?;
|
if let Some(range) = header_span.map_range(cx, |src, range| {
|
||||||
let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?;
|
let mut idxs = src.get(range.clone())?.match_indices('|');
|
||||||
let low = header_span.lo() + BytePos(low_offset as u32);
|
Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1)
|
||||||
let high = low + BytePos(high_offset as u32 + 1);
|
}) {
|
||||||
|
range.with_ctxt(header_span.ctxt())
|
||||||
Some((low, high))
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some((low, high)) = pos {
|
|
||||||
Span::new(low, high, header_span.ctxt(), header_span.parent())
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt};
|
||||||
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
|
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
|
||||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
@ -14,7 +14,7 @@
|
|||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::hygiene::walk_chain;
|
use rustc_span::hygiene::walk_chain;
|
||||||
use rustc_span::source_map::SourceMap;
|
use rustc_span::source_map::SourceMap;
|
||||||
use rustc_span::{BytePos, Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
@ -266,12 +266,12 @@ fn lint_branches_sharing_code<'tcx>(
|
|||||||
|
|
||||||
let span = span.with_hi(last_block.span.hi());
|
let span = span.with_hi(last_block.span.hi());
|
||||||
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
|
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
|
||||||
let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent());
|
let span = span
|
||||||
let span = if snippet_opt(cx, test_span).map_or(false, |snip| snip == " ") {
|
.map_range(cx, |src, range| {
|
||||||
span.with_lo(test_span.lo())
|
(range.start > 4 && src.get(range.start - 4..range.start)? == " ")
|
||||||
} else {
|
.then_some(range.start - 4..range.end)
|
||||||
span
|
})
|
||||||
};
|
.map_or(span, |range| range.with_ctxt(span.ctxt()));
|
||||||
(span, suggestion.to_string())
|
(span, suggestion.to_string())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||||
use clippy_utils::source::{snippet, snippet_opt};
|
use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt};
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
@ -59,10 +59,8 @@
|
|||||||
declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
|
declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
|
impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
|
||||||
#[expect(clippy::cast_possible_truncation, clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
use rustc_span::BytePos;
|
|
||||||
|
|
||||||
fn suggestion(
|
fn suggestion(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
diag: &mut Diag<'_, ()>,
|
diag: &mut Diag<'_, ()>,
|
||||||
@ -123,10 +121,11 @@ fn suggestion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let generics_suggestion_span = impl_.generics.span.substitute_dummy({
|
let generics_suggestion_span = impl_.generics.span.substitute_dummy({
|
||||||
let pos = snippet_opt(cx, item.span.until(target.span()))
|
let range = (item.span.lo()..target.span().lo()).map_range(cx, |src, range| {
|
||||||
.and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
|
Some(src.get(range.clone())?.find("impl")? + 4..range.end)
|
||||||
if let Some(pos) = pos {
|
});
|
||||||
Span::new(pos, pos, item.span.ctxt(), item.span.parent())
|
if let Some(range) = range {
|
||||||
|
range.with_ctxt(item.span.ctxt())
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -163,21 +162,16 @@ fn suggestion(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let generics_suggestion_span = generics.span.substitute_dummy({
|
let generics_suggestion_span = generics.span.substitute_dummy({
|
||||||
let pos = snippet_opt(
|
let range = (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |src, range| {
|
||||||
cx,
|
let (pre, post) = src.get(range.clone())?.split_once("fn")?;
|
||||||
Span::new(
|
let pos = post.find('(')? + pre.len() + 2;
|
||||||
item.span.lo(),
|
Some(pos..pos)
|
||||||
body.params[0].pat.span.lo(),
|
});
|
||||||
item.span.ctxt(),
|
if let Some(range) = range {
|
||||||
item.span.parent(),
|
range.with_ctxt(item.span.ctxt())
|
||||||
),
|
} else {
|
||||||
)
|
return;
|
||||||
.and_then(|snip| {
|
}
|
||||||
let i = snip.find("fn")?;
|
|
||||||
Some(item.span.lo() + BytePos((i + snip[i..].find('(')?) as u32))
|
|
||||||
})
|
|
||||||
.expect("failed to create span for type parameters");
|
|
||||||
Span::new(pos, pos, item.span.ctxt(), item.span.parent())
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
|
let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::{expr_block, get_source_text, snippet};
|
use clippy_utils::source::{expr_block, snippet, SpanRangeExt};
|
||||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
|
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
|
||||||
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
|
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
|
||||||
use core::cmp::max;
|
use core::cmp::max;
|
||||||
@ -17,7 +17,7 @@
|
|||||||
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
|
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
|
||||||
/// match arms.
|
/// match arms.
|
||||||
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
||||||
if let Some(ff) = get_source_text(cx, span)
|
if let Some(ff) = span.get_source_text(cx)
|
||||||
&& let Some(text) = ff.as_str()
|
&& let Some(text) = ff.as_str()
|
||||||
{
|
{
|
||||||
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
|
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::{get_source_text, with_leading_whitespace, SpanRange};
|
use clippy_utils::source::{IntoSpan, SpanRangeExt};
|
||||||
use clippy_utils::ty::get_field_by_name;
|
use clippy_utils::ty::get_field_by_name;
|
||||||
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
|
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
|
||||||
use clippy_utils::{expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, ExprUseNode};
|
use clippy_utils::{expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, ExprUseNode};
|
||||||
@ -9,7 +9,7 @@
|
|||||||
use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind};
|
use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||||
use rustc_span::{sym, BytePos, Span, Symbol, DUMMY_SP};
|
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||||
|
|
||||||
use super::MANUAL_INSPECT;
|
use super::MANUAL_INSPECT;
|
||||||
|
|
||||||
@ -98,17 +98,19 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
|||||||
let mut addr_of_edits = Vec::with_capacity(delayed.len());
|
let mut addr_of_edits = Vec::with_capacity(delayed.len());
|
||||||
for x in delayed {
|
for x in delayed {
|
||||||
match x {
|
match x {
|
||||||
UseKind::Return(s) => edits.push((with_leading_whitespace(cx, s).set_span_pos(s), String::new())),
|
UseKind::Return(s) => edits.push((s.with_leading_whitespace(cx).with_ctxt(s.ctxt()), String::new())),
|
||||||
UseKind::Borrowed(s) => {
|
UseKind::Borrowed(s) => {
|
||||||
if let Some(src) = get_source_text(cx, s)
|
#[expect(clippy::range_plus_one)]
|
||||||
&& let Some(src) = src.as_str()
|
let range = s.map_range(cx, |src, range| {
|
||||||
&& let trim_src = src.trim_start_matches([' ', '\t', '\n', '\r', '('])
|
let src = src.get(range.clone())?;
|
||||||
&& trim_src.starts_with('&')
|
let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']);
|
||||||
{
|
trimmed.starts_with('&').then(|| {
|
||||||
let range = s.into_range();
|
let pos = range.start + src.len() - trimmed.len();
|
||||||
#[expect(clippy::cast_possible_truncation)]
|
pos..pos + 1
|
||||||
let start = BytePos(range.start.0 + (src.len() - trim_src.len()) as u32);
|
})
|
||||||
addr_of_edits.push(((start..BytePos(start.0 + 1)).set_span_pos(s), String::new()));
|
});
|
||||||
|
if let Some(range) = range {
|
||||||
|
addr_of_edits.push((range.with_ctxt(s.ctxt()), String::new()));
|
||||||
} else {
|
} else {
|
||||||
requires_copy = true;
|
requires_copy = true;
|
||||||
requires_deref = true;
|
requires_deref = true;
|
||||||
@ -174,7 +176,10 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
|||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
edits.push((
|
edits.push((
|
||||||
with_leading_whitespace(cx, final_expr.span).set_span_pos(final_expr.span),
|
final_expr
|
||||||
|
.span
|
||||||
|
.with_leading_whitespace(cx)
|
||||||
|
.with_ctxt(final_expr.span.ctxt()),
|
||||||
String::new(),
|
String::new(),
|
||||||
));
|
));
|
||||||
let app = if edits.iter().any(|(s, _)| s.from_expansion()) {
|
let app = if edits.iter().any(|(s, _)| s.from_expansion()) {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
use clippy_utils::attrs::is_doc_hidden;
|
use clippy_utils::attrs::is_doc_hidden;
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::is_from_proc_macro;
|
use clippy_utils::is_from_proc_macro;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::SpanRangeExt;
|
||||||
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
|
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
@ -266,8 +266,5 @@ fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool {
|
fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool {
|
||||||
let Some(snippet) = snippet_opt(cx, search_span) else {
|
search_span.check_source_text(cx, |src| src.lines().rev().any(|line| line.trim().starts_with("///")))
|
||||||
return false;
|
|
||||||
};
|
|
||||||
snippet.lines().rev().any(|line| line.trim().starts_with("///"))
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::SpanRangeExt;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -54,8 +54,10 @@ fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: Node
|
|||||||
match clause {
|
match clause {
|
||||||
WherePredicate::BoundPredicate(pred) => {
|
WherePredicate::BoundPredicate(pred) => {
|
||||||
if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty())
|
if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty())
|
||||||
&& let Some(name) = snippet_opt(cx, pred.bounded_ty.span)
|
&& let Some(Some(bound_span)) = pred
|
||||||
&& let Some(bound_span) = generic_params_with_bounds.get(name.as_str())
|
.bounded_ty
|
||||||
|
.span
|
||||||
|
.with_source_text(cx, |src| generic_params_with_bounds.get(src))
|
||||||
{
|
{
|
||||||
emit_lint(cx, *bound_span, pred.bounded_ty.span);
|
emit_lint(cx, *bound_span, pred.bounded_ty.span);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::{snippet_opt, trim_span};
|
use clippy_utils::source::{IntoSpan, SpanRangeExt};
|
||||||
use rustc_ast::ast::{Expr, ExprKind};
|
use rustc_ast::ast::{Expr, ExprKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
@ -41,16 +41,16 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
|||||||
&& !expr.span.from_expansion()
|
&& !expr.span.from_expansion()
|
||||||
&& !else_clause.span.from_expansion()
|
&& !else_clause.span.from_expansion()
|
||||||
&& block.stmts.is_empty()
|
&& block.stmts.is_empty()
|
||||||
&& let Some(trimmed) = expr.span.trim_start(then_block.span)
|
&& let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx)
|
||||||
&& let span = trim_span(cx.sess().source_map(), trimmed)
|
&& range.clone().check_source_text(cx, |src| {
|
||||||
&& let Some(else_snippet) = snippet_opt(cx, span)
|
// Ignore else blocks that contain comments or #[cfg]s
|
||||||
// Ignore else blocks that contain comments or #[cfg]s
|
!src.contains(['/', '#'])
|
||||||
&& !else_snippet.contains(['/', '#'])
|
})
|
||||||
{
|
{
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
NEEDLESS_ELSE,
|
NEEDLESS_ELSE,
|
||||||
span,
|
range.with_ctxt(expr.span.ctxt()),
|
||||||
"this `else` branch is empty",
|
"this `else` branch is empty",
|
||||||
"you can remove it",
|
"you can remove it",
|
||||||
String::new(),
|
String::new(),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::higher::If;
|
use clippy_utils::higher::If;
|
||||||
use clippy_utils::is_from_proc_macro;
|
use clippy_utils::is_from_proc_macro;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::{snippet_opt, SpanRangeExt};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{ExprKind, Stmt, StmtKind};
|
use rustc_hir::{ExprKind, Stmt, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
@ -39,18 +39,24 @@
|
|||||||
impl LateLintPass<'_> for NeedlessIf {
|
impl LateLintPass<'_> for NeedlessIf {
|
||||||
fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) {
|
fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) {
|
||||||
if let StmtKind::Expr(expr) = stmt.kind
|
if let StmtKind::Expr(expr) = stmt.kind
|
||||||
&& let Some(If {cond, then, r#else: None }) = If::hir(expr)
|
&& let Some(If {
|
||||||
|
cond,
|
||||||
|
then,
|
||||||
|
r#else: None,
|
||||||
|
}) = If::hir(expr)
|
||||||
&& let ExprKind::Block(block, ..) = then.kind
|
&& let ExprKind::Block(block, ..) = then.kind
|
||||||
&& block.stmts.is_empty()
|
&& block.stmts.is_empty()
|
||||||
&& block.expr.is_none()
|
&& block.expr.is_none()
|
||||||
&& !in_external_macro(cx.sess(), expr.span)
|
&& !in_external_macro(cx.sess(), expr.span)
|
||||||
&& let Some(then_snippet) = snippet_opt(cx, then.span)
|
&& then.span.check_source_text(cx, |src| {
|
||||||
// Ignore
|
// Ignore
|
||||||
// - empty macro expansions
|
// - empty macro expansions
|
||||||
// - empty reptitions in macro expansions
|
// - empty reptitions in macro expansions
|
||||||
// - comments
|
// - comments
|
||||||
// - #[cfg]'d out code
|
// - #[cfg]'d out code
|
||||||
&& then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace())
|
src.bytes()
|
||||||
|
.all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace())
|
||||||
|
})
|
||||||
&& let Some(cond_snippet) = snippet_opt(cx, cond.span)
|
&& let Some(cond_snippet) = snippet_opt(cx, cond.span)
|
||||||
&& !is_from_proc_macro(cx, expr)
|
&& !is_from_proc_macro(cx, expr)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
use clippy_utils::source::{snippet_with_applicability, SpanRangeExt};
|
||||||
use clippy_utils::{match_def_path, paths};
|
use clippy_utils::{match_def_path, paths};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
@ -53,8 +53,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||||||
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
|
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
|
||||||
&& let ExprKind::Lit(_) = param.kind
|
&& let ExprKind::Lit(_) = param.kind
|
||||||
&& param.span.eq_ctxt(expr.span)
|
&& param.span.eq_ctxt(expr.span)
|
||||||
&& let Some(snip) = snippet_opt(cx, param.span)
|
&& param
|
||||||
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
|
.span
|
||||||
|
.check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..]))
|
||||||
{
|
{
|
||||||
show_error(cx, param);
|
show_error(cx, param);
|
||||||
}
|
}
|
||||||
@ -65,8 +66,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||||||
&& match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE)
|
&& match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE)
|
||||||
&& let ExprKind::Lit(_) = param.kind
|
&& let ExprKind::Lit(_) = param.kind
|
||||||
&& param.span.eq_ctxt(expr.span)
|
&& param.span.eq_ctxt(expr.span)
|
||||||
&& let Some(snip) = snippet_opt(cx, param.span)
|
&& param
|
||||||
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
|
.span
|
||||||
|
.check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..]))
|
||||||
{
|
{
|
||||||
show_error(cx, param);
|
show_error(cx, param);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::get_source_text;
|
use clippy_utils::source::SpanRangeExt;
|
||||||
use rustc_ast::token::LitKind;
|
use rustc_ast::token::LitKind;
|
||||||
use rustc_ast::{Expr, ExprKind};
|
use rustc_ast::{Expr, ExprKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
@ -87,14 +87,11 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
|||||||
|
|
||||||
// Last check to make sure the source text matches what we read from the string.
|
// Last check to make sure the source text matches what we read from the string.
|
||||||
// Macros are involved somehow if this doesn't match.
|
// Macros are involved somehow if this doesn't match.
|
||||||
if let Some(src) = get_source_text(cx, span)
|
if span.check_source_text(cx, |src| match *src.as_bytes() {
|
||||||
&& let Some(src) = src.as_str()
|
[b'\\', b'0', lo] => lo == c_lo,
|
||||||
&& match *src.as_bytes() {
|
[b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo,
|
||||||
[b'\\', b'0', lo] => lo == c_lo,
|
_ => false,
|
||||||
[b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo,
|
}) {
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
{
|
|
||||||
span_lint_and_then(cx, OCTAL_ESCAPES, span, "octal-looking escape in a literal", |diag| {
|
span_lint_and_then(cx, OCTAL_ESCAPES, span, "octal-looking escape in a literal", |diag| {
|
||||||
diag.help_once("octal escapes are not supported, `\\0` is always null")
|
diag.help_once("octal escapes are not supported, `\\0` is always null")
|
||||||
.span_suggestion(
|
.span_suggestion(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local};
|
use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local};
|
||||||
use rustc_ast::ast::RangeLimits;
|
use rustc_ast::ast::RangeLimits;
|
||||||
@ -285,9 +285,10 @@ fn check_possible_range_contains(
|
|||||||
if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind
|
if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind
|
||||||
&& op == lhs_op.node
|
&& op == lhs_op.node
|
||||||
&& let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent())
|
&& let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent())
|
||||||
&& let Some(snip) = &snippet_opt(cx, new_span)
|
&& new_span.check_source_text(cx, |src| {
|
||||||
// Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
|
// Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
|
||||||
&& snip.matches('(').count() == snip.matches(')').count()
|
src.matches('(').count() == src.matches(')').count()
|
||||||
|
})
|
||||||
{
|
{
|
||||||
check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
|
check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
|
||||||
}
|
}
|
||||||
@ -363,17 +364,19 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||||||
|diag| {
|
|diag| {
|
||||||
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
|
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
|
||||||
let end = Sugg::hir(cx, y, "y").maybe_par();
|
let end = Sugg::hir(cx, y, "y").maybe_par();
|
||||||
if let Some(is_wrapped) = &snippet_opt(cx, span) {
|
match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) {
|
||||||
if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
|
Some(true) => {
|
||||||
diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect);
|
diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect);
|
||||||
} else {
|
},
|
||||||
|
Some(false) => {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
span,
|
span,
|
||||||
"use",
|
"use",
|
||||||
format!("{start}..={end}"),
|
format!("{start}..={end}"),
|
||||||
Applicability::MachineApplicable, // snippet
|
Applicability::MachineApplicable, // snippet
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
None => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
|
|
||||||
use crate::macros::HirNode;
|
use crate::macros::HirNode;
|
||||||
use crate::source::{get_source_text, walk_span_to_context};
|
use crate::source::{walk_span_to_context, SpanRangeExt};
|
||||||
use crate::{clip, is_direct_expn_of, sext, unsext};
|
use crate::{clip, is_direct_expn_of, sext, unsext};
|
||||||
|
|
||||||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||||
@ -15,8 +15,8 @@
|
|||||||
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy};
|
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy};
|
||||||
use rustc_middle::{bug, mir, span_bug};
|
use rustc_middle::{bug, mir, span_bug};
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
|
use rustc_span::sym;
|
||||||
use rustc_span::symbol::{Ident, Symbol};
|
use rustc_span::symbol::{Ident, Symbol};
|
||||||
use rustc_span::{sym, SyntaxContext};
|
|
||||||
use rustc_target::abi::Size;
|
use rustc_target::abi::Size;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@ -664,11 +664,11 @@ fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> {
|
|||||||
{
|
{
|
||||||
// Try to detect any `cfg`ed statements or empty macro expansions.
|
// Try to detect any `cfg`ed statements or empty macro expansions.
|
||||||
let span = block.span.data();
|
let span = block.span.data();
|
||||||
if span.ctxt == SyntaxContext::root() {
|
if span.ctxt.is_root() {
|
||||||
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
|
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
|
||||||
&& let expr_lo = expr_span.lo()
|
&& let expr_lo = expr_span.lo()
|
||||||
&& expr_lo >= span.lo
|
&& expr_lo >= span.lo
|
||||||
&& let Some(src) = get_source_text(self.lcx, span.lo..expr_lo)
|
&& let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx)
|
||||||
&& let Some(src) = src.as_str()
|
&& let Some(src) = src.as_str()
|
||||||
{
|
{
|
||||||
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
|
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::consts::constant_simple;
|
use crate::consts::constant_simple;
|
||||||
use crate::macros::macro_backtrace;
|
use crate::macros::macro_backtrace;
|
||||||
use crate::source::{get_source_text, snippet_opt, walk_span_to_context, SpanRange};
|
use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt};
|
||||||
use crate::tokenize_with_text;
|
use crate::tokenize_with_text;
|
||||||
use rustc_ast::ast::InlineAsmTemplatePiece;
|
use rustc_ast::ast::InlineAsmTemplatePiece;
|
||||||
use rustc_data_structures::fx::FxHasher;
|
use rustc_data_structures::fx::FxHasher;
|
||||||
@ -1173,9 +1173,9 @@ fn eq_span_tokens(
|
|||||||
pred: impl Fn(TokenKind) -> bool,
|
pred: impl Fn(TokenKind) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
|
fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
|
||||||
if let Some(lsrc) = get_source_text(cx, left)
|
if let Some(lsrc) = left.get_source_text(cx)
|
||||||
&& let Some(lsrc) = lsrc.as_str()
|
&& let Some(lsrc) = lsrc.as_str()
|
||||||
&& let Some(rsrc) = get_source_text(cx, right)
|
&& let Some(rsrc) = right.get_source_text(cx)
|
||||||
&& let Some(rsrc) = rsrc.as_str()
|
&& let Some(rsrc) = rsrc.as_str()
|
||||||
{
|
{
|
||||||
let pred = |t: &(_, _)| pred(t.0);
|
let pred = |t: &(_, _)| pred(t.0);
|
||||||
|
@ -9,22 +9,17 @@
|
|||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::source_map::{original_sp, SourceMap};
|
use rustc_span::source_map::{original_sp, SourceMap};
|
||||||
use rustc_span::{hygiene, BytePos, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, DUMMY_SP};
|
use rustc_span::{
|
||||||
|
hygiene, BytePos, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext,
|
||||||
|
DUMMY_SP,
|
||||||
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
/// A type which can be converted to the range portion of a `Span`.
|
/// Conversion of a value into the range portion of a `Span`.
|
||||||
pub trait SpanRange: Sized {
|
pub trait SpanRange: Sized {
|
||||||
fn into_range(self) -> Range<BytePos>;
|
fn into_range(self) -> Range<BytePos>;
|
||||||
fn set_span_pos(self, sp: Span) -> Span {
|
|
||||||
let range = self.into_range();
|
|
||||||
SpanData {
|
|
||||||
lo: range.start,
|
|
||||||
hi: range.end,
|
|
||||||
..sp.data()
|
|
||||||
}
|
|
||||||
.span()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl SpanRange for Span {
|
impl SpanRange for Span {
|
||||||
fn into_range(self) -> Range<BytePos> {
|
fn into_range(self) -> Range<BytePos> {
|
||||||
@ -43,6 +38,182 @@ fn into_range(self) -> Range<BytePos> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Conversion of a value into a `Span`
|
||||||
|
pub trait IntoSpan: Sized {
|
||||||
|
fn into_span(self) -> Span;
|
||||||
|
fn with_ctxt(self, ctxt: SyntaxContext) -> Span;
|
||||||
|
}
|
||||||
|
impl IntoSpan for Span {
|
||||||
|
fn into_span(self) -> Span {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
|
||||||
|
self.with_ctxt(ctxt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IntoSpan for SpanData {
|
||||||
|
fn into_span(self) -> Span {
|
||||||
|
self.span()
|
||||||
|
}
|
||||||
|
fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
|
||||||
|
Span::new(self.lo, self.hi, ctxt, self.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IntoSpan for Range<BytePos> {
|
||||||
|
fn into_span(self) -> Span {
|
||||||
|
Span::with_root_ctxt(self.start, self.end)
|
||||||
|
}
|
||||||
|
fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
|
||||||
|
Span::new(self.start, self.end, ctxt, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SpanRangeExt: SpanRange {
|
||||||
|
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span
|
||||||
|
/// extends through multiple files, or is malformed.
|
||||||
|
fn get_source_text(self, cx: &impl LintContext) -> Option<SourceFileRange> {
|
||||||
|
get_source_text(cx.sess().source_map(), self.into_range())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the given function with the source text referenced and returns the value. Returns
|
||||||
|
/// `None` if the source text cannot be retrieved.
|
||||||
|
fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
|
||||||
|
with_source_text(cx.sess().source_map(), self.into_range(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the referenced source text satisfies the given predicate. Returns `false` if the
|
||||||
|
/// source text cannot be retrieved.
|
||||||
|
fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
|
||||||
|
self.with_source_text(cx, pred).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the given function with the both the text of the source file and the referenced range,
|
||||||
|
/// and returns the value. Returns `None` if the source text cannot be retrieved.
|
||||||
|
fn with_source_text_and_range<T>(
|
||||||
|
self,
|
||||||
|
cx: &impl LintContext,
|
||||||
|
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
|
||||||
|
) -> Option<T> {
|
||||||
|
with_source_text_and_range(cx.sess().source_map(), self.into_range(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the given function with the both the text of the source file and the referenced range,
|
||||||
|
/// and creates a new span with the returned range. Returns `None` if the source text cannot be
|
||||||
|
/// retrieved, or no result is returned.
|
||||||
|
///
|
||||||
|
/// The new range must reside within the same source file.
|
||||||
|
fn map_range(
|
||||||
|
self,
|
||||||
|
cx: &impl LintContext,
|
||||||
|
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
|
||||||
|
) -> Option<Range<BytePos>> {
|
||||||
|
map_range(cx.sess().source_map(), self.into_range(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extends the range to include all preceding whitespace characters.
|
||||||
|
fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> {
|
||||||
|
with_leading_whitespace(cx.sess().source_map(), self.into_range())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trims the leading whitespace from the range.
|
||||||
|
fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> {
|
||||||
|
trim_start(cx.sess().source_map(), self.into_range())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the referenced source text to the given writer. Will return `Err` if the source text
|
||||||
|
/// could not be retrieved.
|
||||||
|
fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result {
|
||||||
|
write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts the referenced source text as an owned string.
|
||||||
|
fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> {
|
||||||
|
self.with_source_text(cx, ToOwned::to_owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: SpanRange> SpanRangeExt for T {}
|
||||||
|
|
||||||
|
fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
|
||||||
|
let start = sm.lookup_byte_offset(sp.start);
|
||||||
|
let end = sm.lookup_byte_offset(sp.end);
|
||||||
|
if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let range = start.pos.to_usize()..end.pos.to_usize();
|
||||||
|
Some(SourceFileRange { sf: start.sf, range })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
|
||||||
|
if let Some(src) = get_source_text(sm, sp)
|
||||||
|
&& let Some(src) = src.as_str()
|
||||||
|
{
|
||||||
|
Some(f(src))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_source_text_and_range<T>(
|
||||||
|
sm: &SourceMap,
|
||||||
|
sp: Range<BytePos>,
|
||||||
|
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
|
||||||
|
) -> Option<T> {
|
||||||
|
if let Some(src) = get_source_text(sm, sp)
|
||||||
|
&& let Some(text) = &src.sf.src
|
||||||
|
{
|
||||||
|
Some(f(text, src.range))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::cast_possible_truncation)]
|
||||||
|
fn map_range(
|
||||||
|
sm: &SourceMap,
|
||||||
|
sp: Range<BytePos>,
|
||||||
|
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
|
||||||
|
) -> Option<Range<BytePos>> {
|
||||||
|
if let Some(src) = get_source_text(sm, sp.clone())
|
||||||
|
&& let Some(text) = &src.sf.src
|
||||||
|
&& let Some(range) = f(text, src.range.clone())
|
||||||
|
{
|
||||||
|
debug_assert!(
|
||||||
|
range.start <= text.len() && range.end <= text.len(),
|
||||||
|
"Range `{range:?}` is outside the source file (file `{}`, length `{}`)",
|
||||||
|
src.sf.name.display(FileNameDisplayPreference::Local),
|
||||||
|
text.len(),
|
||||||
|
);
|
||||||
|
debug_assert!(range.start <= range.end, "Range `{range:?}` has overlapping bounds");
|
||||||
|
let dstart = (range.start as u32).wrapping_sub(src.range.start as u32);
|
||||||
|
let dend = (range.end as u32).wrapping_sub(src.range.start as u32);
|
||||||
|
Some(BytePos(sp.start.0.wrapping_add(dstart))..BytePos(sp.start.0.wrapping_add(dend)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_leading_whitespace(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
|
||||||
|
map_range(sm, sp.clone(), |src, range| {
|
||||||
|
Some(src.get(..range.start)?.trim_end().len()..range.end)
|
||||||
|
})
|
||||||
|
.unwrap_or(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
|
||||||
|
map_range(sm, sp.clone(), |src, range| {
|
||||||
|
let src = src.get(range.clone())?;
|
||||||
|
Some(range.start + (src.len() - src.trim_start().len())..range.end)
|
||||||
|
})
|
||||||
|
.unwrap_or(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::Write) -> fmt::Result {
|
||||||
|
match with_source_text(sm, sp, |src| dst.write_str(src)) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => Err(fmt::Error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SourceFileRange {
|
pub struct SourceFileRange {
|
||||||
pub sf: Lrc<SourceFile>,
|
pub sf: Lrc<SourceFile>,
|
||||||
pub range: Range<usize>,
|
pub range: Range<usize>,
|
||||||
@ -55,37 +226,6 @@ pub fn as_str(&self) -> Option<&str> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span
|
|
||||||
/// extends through multiple files, or is malformed.
|
|
||||||
pub fn get_source_text(cx: &impl LintContext, sp: impl SpanRange) -> Option<SourceFileRange> {
|
|
||||||
fn f(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
|
|
||||||
let start = sm.lookup_byte_offset(sp.start);
|
|
||||||
let end = sm.lookup_byte_offset(sp.end);
|
|
||||||
if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let range = start.pos.to_usize()..end.pos.to_usize();
|
|
||||||
Some(SourceFileRange { sf: start.sf, range })
|
|
||||||
}
|
|
||||||
f(cx.sess().source_map(), sp.into_range())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_leading_whitespace(cx: &impl LintContext, sp: impl SpanRange) -> Range<BytePos> {
|
|
||||||
#[expect(clippy::needless_pass_by_value, clippy::cast_possible_truncation)]
|
|
||||||
fn f(src: SourceFileRange, sp: Range<BytePos>) -> Range<BytePos> {
|
|
||||||
let Some(text) = &src.sf.src else {
|
|
||||||
return sp;
|
|
||||||
};
|
|
||||||
let len = src.range.start - text[..src.range.start].trim_end().len();
|
|
||||||
BytePos(sp.start.0 - len as u32)..sp.end
|
|
||||||
}
|
|
||||||
let sp = sp.into_range();
|
|
||||||
match get_source_text(cx, sp.clone()) {
|
|
||||||
Some(src) => f(src, sp),
|
|
||||||
None => sp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||||
pub fn expr_block<T: LintContext>(
|
pub fn expr_block<T: LintContext>(
|
||||||
cx: &T,
|
cx: &T,
|
||||||
|
Loading…
Reference in New Issue
Block a user