Skip to content

Add new span related utils #12972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions clippy_lints/src/cognitive_complexity.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! calculate cognitive complexity and warn about overly complex functions

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::visitors::for_each_expr_without_closures;
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
Expand All @@ -12,7 +12,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, BytePos, Span};
use rustc_span::{sym, Span};

declare_clippy_lint! {
/// ### What it does
Expand Down Expand Up @@ -50,7 +50,6 @@ impl CognitiveComplexity {
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);

impl CognitiveComplexity {
#[expect(clippy::cast_possible_truncation)]
fn check<'tcx>(
&mut self,
cx: &LateContext<'tcx>,
Expand Down Expand Up @@ -100,17 +99,12 @@ impl CognitiveComplexity {
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
FnKind::Closure => {
let header_span = body_span.with_hi(decl.output.span().lo());
let pos = snippet_opt(cx, header_span).and_then(|snip| {
let low_offset = snip.find('|')?;
let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?;
let low = header_span.lo() + BytePos(low_offset as u32);
let high = low + BytePos(high_offset as u32 + 1);

Some((low, high))
});

if let Some((low, high)) = pos {
Span::new(low, high, header_span.ctxt(), header_span.parent())
#[expect(clippy::range_plus_one)]
if let Some(range) = header_span.map_range(cx, |src, range| {
let mut idxs = src.get(range.clone())?.match_indices('|');
Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1)
}) {
range.with_ctxt(header_span.ctxt())
} else {
return;
}
Expand Down
16 changes: 8 additions & 8 deletions clippy_lints/src/copies.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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::visitors::for_each_expr_without_closures;
use clippy_utils::{
Expand All @@ -14,7 +14,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::hygiene::walk_chain;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Span, Symbol};
use rustc_span::{Span, Symbol};
use std::borrow::Cow;

declare_clippy_lint! {
Expand Down Expand Up @@ -266,12 +266,12 @@ fn lint_branches_sharing_code<'tcx>(

let span = span.with_hi(last_block.span.hi());
// 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 = if snippet_opt(cx, test_span).map_or(false, |snip| snip == " ") {
span.with_lo(test_span.lo())
} else {
span
};
let span = span
.map_range(cx, |src, range| {
(range.start > 4 && src.get(range.start - 4..range.start)? == " ")
.then_some(range.start - 4..range.end)
})
.map_or(span, |range| range.with_ctxt(span.ctxt()));
(span, suggestion.to_string())
});

Expand Down
40 changes: 17 additions & 23 deletions clippy_lints/src/implicit_hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_span::symbol::sym;
use rustc_span::Span;

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;

declare_clippy_lint! {
Expand Down Expand Up @@ -59,10 +59,8 @@ declare_clippy_lint! {
declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);

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<'_>) {
use rustc_span::BytePos;

fn suggestion(
cx: &LateContext<'_>,
diag: &mut Diag<'_, ()>,
Expand Down Expand Up @@ -123,10 +121,11 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
}

let generics_suggestion_span = impl_.generics.span.substitute_dummy({
let pos = snippet_opt(cx, item.span.until(target.span()))
.and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
if let Some(pos) = pos {
Span::new(pos, pos, item.span.ctxt(), item.span.parent())
let range = (item.span.lo()..target.span().lo()).map_range(cx, |src, range| {
Some(src.get(range.clone())?.find("impl")? + 4..range.end)
});
if let Some(range) = range {
range.with_ctxt(item.span.ctxt())
} else {
return;
}
Expand Down Expand Up @@ -163,21 +162,16 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
continue;
}
let generics_suggestion_span = generics.span.substitute_dummy({
let pos = snippet_opt(
cx,
Span::new(
item.span.lo(),
body.params[0].pat.span.lo(),
item.span.ctxt(),
item.span.parent(),
),
)
.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 range = (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |src, range| {
let (pre, post) = src.get(range.clone())?.split_once("fn")?;
let pos = post.find('(')? + pre.len() + 2;
Some(pos..pos)
});
if let Some(range) = range {
range.with_ctxt(item.span.ctxt())
} else {
return;
}
});

let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/matches/single_match.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
use core::cmp::max;
Expand All @@ -17,7 +17,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
/// match arms.
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()
{
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
Expand Down
31 changes: 18 additions & 13 deletions clippy_lints/src/methods/manual_inspect.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
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::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};
Expand All @@ -9,7 +9,7 @@ use rustc_errors::Applicability;
use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind};
use rustc_lint::LateContext;
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;

Expand Down Expand Up @@ -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());
for x in delayed {
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) => {
if let Some(src) = get_source_text(cx, s)
&& let Some(src) = src.as_str()
&& let trim_src = src.trim_start_matches([' ', '\t', '\n', '\r', '('])
&& trim_src.starts_with('&')
{
let range = s.into_range();
#[expect(clippy::cast_possible_truncation)]
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()));
#[expect(clippy::range_plus_one)]
let range = s.map_range(cx, |src, range| {
let src = src.get(range.clone())?;
let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']);
trimmed.starts_with('&').then(|| {
let pos = range.start + src.len() - trimmed.len();
pos..pos + 1
})
});
if let Some(range) = range {
addr_of_edits.push((range.with_ctxt(s.ctxt()), String::new()));
} else {
requires_copy = true;
requires_deref = true;
Expand Down Expand Up @@ -174,7 +176,10 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
}),
));
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(),
));
let app = if edits.iter().any(|(s, _)| s.from_expansion()) {
Expand Down
7 changes: 2 additions & 5 deletions clippy_lints/src/missing_doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint;
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_hir as hir;
use rustc_hir::def_id::LocalDefId;
Expand Down Expand Up @@ -266,8 +266,5 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
}

fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool {
let Some(snippet) = snippet_opt(cx, search_span) else {
return false;
};
snippet.lines().rev().any(|line| line.trim().starts_with("///"))
search_span.check_source_text(cx, |src| src.lines().rev().any(|line| line.trim().starts_with("///")))
}
8 changes: 5 additions & 3 deletions clippy_lints/src/multiple_bound_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass;
use rustc_span::Span;

use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;

declare_clippy_lint! {
/// ### What it does
Expand Down Expand Up @@ -54,8 +54,10 @@ impl EarlyLintPass for MultipleBoundLocations {
match clause {
WherePredicate::BoundPredicate(pred) => {
if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty())
&& let Some(name) = snippet_opt(cx, pred.bounded_ty.span)
&& let Some(bound_span) = generic_params_with_bounds.get(name.as_str())
&& let Some(Some(bound_span)) = pred
.bounded_ty
.span
.with_source_text(cx, |src| generic_params_with_bounds.get(src))
{
emit_lint(cx, *bound_span, pred.bounded_ty.span);
}
Expand Down
16 changes: 8 additions & 8 deletions clippy_lints/src/needless_else.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
Expand Down Expand Up @@ -41,16 +41,16 @@ impl EarlyLintPass for NeedlessElse {
&& !expr.span.from_expansion()
&& !else_clause.span.from_expansion()
&& block.stmts.is_empty()
&& let Some(trimmed) = expr.span.trim_start(then_block.span)
&& let span = trim_span(cx.sess().source_map(), trimmed)
&& let Some(else_snippet) = snippet_opt(cx, span)
// Ignore else blocks that contain comments or #[cfg]s
&& !else_snippet.contains(['/', '#'])
&& let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx)
&& range.clone().check_source_text(cx, |src| {
// Ignore else blocks that contain comments or #[cfg]s
!src.contains(['/', '#'])
})
{
span_lint_and_sugg(
cx,
NEEDLESS_ELSE,
span,
range.with_ctxt(expr.span.ctxt()),
"this `else` branch is empty",
"you can remove it",
String::new(),
Expand Down
24 changes: 15 additions & 9 deletions clippy_lints/src/needless_if.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher::If;
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_hir::{ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
Expand Down Expand Up @@ -39,18 +39,24 @@ declare_lint_pass!(NeedlessIf => [NEEDLESS_IF]);
impl LateLintPass<'_> for NeedlessIf {
fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) {
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
&& block.stmts.is_empty()
&& block.expr.is_none()
&& !in_external_macro(cx.sess(), expr.span)
&& let Some(then_snippet) = snippet_opt(cx, then.span)
// Ignore
// - empty macro expansions
// - empty reptitions in macro expansions
// - comments
// - #[cfg]'d out code
&& then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace())
&& then.span.check_source_text(cx, |src| {
// Ignore
// - empty macro expansions
// - empty reptitions in macro expansions
// - comments
// - #[cfg]'d out code
src.bytes()
.all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace())
})
&& let Some(cond_snippet) = snippet_opt(cx, cond.span)
&& !is_from_proc_macro(cx, expr)
{
Expand Down
12 changes: 7 additions & 5 deletions clippy_lints/src/non_octal_unix_permissions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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 rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
Expand Down Expand Up @@ -53,8 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
&& let ExprKind::Lit(_) = param.kind
&& param.span.eq_ctxt(expr.span)
&& let Some(snip) = snippet_opt(cx, param.span)
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
&& param
.span
.check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..]))
{
show_error(cx, param);
}
Expand All @@ -65,8 +66,9 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
&& match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE)
&& let ExprKind::Lit(_) = param.kind
&& param.span.eq_ctxt(expr.span)
&& let Some(snip) = snippet_opt(cx, param.span)
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
&& param
.span
.check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..]))
{
show_error(cx, param);
}
Expand Down
Loading