Skip to content
Open
40 changes: 40 additions & 0 deletions clippy_utils/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use std::iter;
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
pub enum Constant {
/// A constant representing an algebraic data type
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is any ADT or just structs, because mir_to_const() seems only to convert struct ADTs.

Adt(ConstValue),
/// A `String` (e.g., "abc").
Str(String),
Expand Down Expand Up @@ -203,6 +204,15 @@ impl Hash for Constant {
}

impl Constant {
/// Returns an ordering between `left` and `right`, if one exists. `cmp_type` determines
/// comparison behavior for constants with ambiguous typing (ex. [`Constant::Int`], which may be
/// signed or unsigned).
///
/// # Panics
///
/// Panics if the compared type is ambiguous and `cmp_type` describes a nonsensical type. For
/// example, if `left` and `right` are [`Constant::Int`], `cmp_type.kind()` must be either
/// [`Int`](ty::Int) or [`Uint`](ty::Uint).
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)),
Expand Down Expand Up @@ -273,6 +283,7 @@ impl Constant {
}
}

/// Recursively consume [`Constant::Ref`] and return the innermost value.
Copy link
Author

@SignalWalker SignalWalker Oct 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "Recursively" is technically the right word, but I couldn't think of anything better that would still be succinct.

#[must_use]
pub fn peel_refs(mut self) -> Self {
while let Constant::Ref(r) = self {
Expand All @@ -291,6 +302,8 @@ impl Constant {
Self::F128(f.to_bits())
}

/// Create a constant representing the minimum value of the type described by `ty`, if one
/// is defined.
pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Self> {
match *ty.kind() {
ty::Uint(_) => Some(Self::Int(0)),
Expand All @@ -312,6 +325,8 @@ impl Constant {
}
}

/// Create a constant representing the maximum value of the type described by `ty`, if one
/// is defined.
pub fn new_numeric_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Self> {
match *ty.kind() {
ty::Uint(ty) => Some(Self::Int(match ty.normalize(tcx.sess.target.pointer_width) {
Expand Down Expand Up @@ -340,6 +355,8 @@ impl Constant {
}
}

/// Checks whether `self` is a numeric of the type `ty` and whether `self` is the minimum value
/// of that type.
pub fn is_numeric_min<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match (self, ty.kind()) {
(&Self::Int(x), &ty::Uint(_)) => x == 0,
Expand All @@ -361,6 +378,8 @@ impl Constant {
}
}

/// Checks whether `self` is a numeric of the type `ty` and whether `self` is the maximum value
/// of that type.
pub fn is_numeric_max<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match (self, ty.kind()) {
(&Self::Int(x), &ty::Uint(ty)) => {
Expand Down Expand Up @@ -392,6 +411,7 @@ impl Constant {
}
}

/// Checks whether `self` is a floating-point value representing positive infinity.
pub fn is_pos_infinity(&self) -> bool {
match *self {
// FIXME(f16_f128): add f16 and f128 when constants are available
Expand All @@ -401,6 +421,7 @@ impl Constant {
}
}

/// Checks whether `self` is a floating-point value representing negative infinity.
pub fn is_neg_infinity(&self) -> bool {
match *self {
// FIXME(f16_f128): add f16 and f128 when constants are available
Expand Down Expand Up @@ -448,14 +469,20 @@ pub enum ConstantSource {
NonLocal,
}
impl ConstantSource {
/// Checks whether this constant value is determined solely from its expression, and is not
/// dependent on another definition.
pub fn is_local(self) -> bool {
matches!(self, Self::Local)
}
}

/// An integer type (signed or unsigned) with enough bits to represent any integer that can be
/// represented in Rust.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is what this is for, based on the name and from skimming over usage.

#[derive(Copy, Clone, Debug, Eq)]
pub enum FullInt {
/// Signed full int
S(i128),
/// Unsigned full int
U(u128),
}

Expand Down Expand Up @@ -564,6 +591,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}

/// Attempts to evaluate the given [pattern expression](PatExpr) as a [`Constant`].
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure this is accurate?

pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant> {
match &pat_expr.kind {
PatExprKind::Lit { lit, negated } => {
Expand Down Expand Up @@ -1047,6 +1075,18 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}

/// Converts a [`ConstValue`] to a [`Constant`], if possible.
///
/// - If `ty` is an [`Adt`](ty::Adt) describing a struct, returns a [`Constant::Adt`] containing
/// `val`.
/// - If `val` is a [`Int`](Scalar::Int), `ty` determines the variant of the returned constant.
/// Returns `None` if `val` cannot be converted to the type described by `ty`.
/// - If `ty` is a [`Ref`](ty::Ref) referring to a [`Str`](ty::Str) (i.e. a `&str`), returns a
/// [`Constant::Str`].
/// - If `val` is a [`ConstValue::Indirect`] and `ty` is an [`Array`](ty::Array) of
/// [`Float`](ty::Float), returns a [`Constant::Vec`]
///
/// Otherwise, returns `None`.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function actually as particular as described or am I missing something

pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> Option<Constant> {
match (val, ty.kind()) {
(_, &ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(val)),
Expand Down
3 changes: 3 additions & 0 deletions clippy_utils/src/higher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ impl<'hir> IfLetOrMatch<'hir> {
}
}

/// Returns the [expression](Expr) scrutinized by this `if let` or `match` expression.
pub fn scrutinee(&self) -> &'hir Expr<'hir> {
match self {
Self::Match(scrutinee, _, _) | Self::IfLet(scrutinee, _, _, _, _) => scrutinee,
Expand Down Expand Up @@ -319,6 +320,7 @@ pub struct While<'hir> {
pub body: &'hir Expr<'hir>,
/// Span of the loop header
pub span: Span,
/// The loop's label, if present
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub label: Option<ast::Label>,
}

Expand Down Expand Up @@ -360,6 +362,7 @@ pub struct WhileLet<'hir> {
pub let_expr: &'hir Expr<'hir>,
/// `while let` loop body
pub if_then: &'hir Expr<'hir>,
/// The loop's label, if present
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub label: Option<ast::Label>,
/// `while let PAT = EXPR`
/// ^^^^^^^^^^^^^^
Expand Down
7 changes: 7 additions & 0 deletions clippy_utils/src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Utilities for analyzing macro invocations and expansions.

#![allow(clippy::similar_names)] // `expr` and `expn`

use std::sync::{Arc, OnceLock};
Expand Down Expand Up @@ -65,6 +67,7 @@ pub struct MacroCall {
}

impl MacroCall {
/// Returns true if this macro call is from the root expansion or a locally defined macro
pub fn is_local(&self) -> bool {
span_is_local(self.span)
}
Expand Down Expand Up @@ -228,6 +231,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
matches!(name, sym::assert_macro | sym::debug_assert_macro)
}

/// A `panic!()` expression, which may contain arguments
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this one

#[derive(Debug)]
pub enum PanicExpn<'a> {
/// No arguments - `panic!()`
Expand All @@ -241,6 +245,7 @@ pub enum PanicExpn<'a> {
}

impl<'a> PanicExpn<'a> {
/// Parses a `panic!()` expression
pub fn parse(expr: &'a Expr<'a>) -> Option<Self> {
let ExprKind::Call(callee, args) = &expr.kind else {
return None;
Expand Down Expand Up @@ -516,7 +521,9 @@ pub enum FormatParamUsage {

/// A node with a `HirId` and a `Span`
pub trait HirNode {
/// Returns this node's [`HirId`]
fn hir_id(&self) -> HirId;
/// Returns this node's [`Span`]
fn span(&self) -> Span;
}

Expand Down
3 changes: 3 additions & 0 deletions clippy_utils/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ use std::sync::OnceLock;
/// arbitrary namespace
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PathNS {
/// The [type namespace](TypeNS)
Type,
/// The [value namespace](ValueNS)
Value,
/// The [macro namespace](MacroNS)
Macro,

/// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return
Expand Down
5 changes: 5 additions & 0 deletions clippy_utils/src/res.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Utilities for node resolution.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this fully describes this module?


use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{
Expand All @@ -10,6 +12,7 @@ use rustc_span::{Ident, Symbol};

/// Either a `HirId` or a type which can be identified by one.
pub trait HasHirId: Copy {
/// Returns the [`HirId`] identifying `self`.
fn hir_id(self) -> HirId;
}
impl HasHirId for HirId {
Expand All @@ -27,6 +30,7 @@ impl HasHirId for &Expr<'_> {

type DefRes = (DefKind, DefId);

/// Either a [`TypeckResults`] or a type that may contain one.
pub trait MaybeTypeckRes<'tcx> {
/// Gets the contained `TypeckResults`.
///
Expand Down Expand Up @@ -469,6 +473,7 @@ impl<'a, T: MaybeResPath<'a>> MaybeResPath<'a> for Option<T> {

/// A type which may either contain a `DefId` or be referred to by a `DefId`.
pub trait MaybeDef: Copy {
/// Gets the [`DefId`] contained by or referring to `self`
fn opt_def_id(self) -> Option<DefId>;

/// Gets this definition's id and kind. This will lookup the kind in the def
Expand Down
16 changes: 16 additions & 0 deletions clippy_utils/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use std::borrow::Cow;
use std::fmt;
use std::ops::{Deref, Index, Range};

/// Either a [`Session`] or a type which can be associated with one.
pub trait HasSession {
/// Gets the [`Session`] associated with `self`.
fn sess(&self) -> &Session;
}
impl HasSession for Session {
Expand All @@ -46,6 +48,7 @@ impl HasSession for LateContext<'_> {

/// Conversion of a value into the range portion of a `Span`.
pub trait SpanRange: Sized {
/// Converts `self` into the range portion of a [`Span`].
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this one

fn into_range(self) -> Range<BytePos>;
}
impl SpanRange for Span {
Expand All @@ -67,7 +70,9 @@ impl SpanRange for Range<BytePos> {

/// Conversion of a value into a `Span`
pub trait IntoSpan: Sized {
/// Converts `self` into a [`Span`].
fn into_span(self) -> Span;
/// Converts `self` into a [`Span`], with [context](SyntaxContext).
fn with_ctxt(self, ctxt: SyntaxContext) -> Span;
}
impl IntoSpan for Span {
Expand Down Expand Up @@ -95,6 +100,7 @@ impl IntoSpan for Range<BytePos> {
}
}

/// Extensions to [`SpanRange`].
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could probably be more descriptive

pub trait SpanRangeExt: SpanRange {
/// Attempts to get a handle to the source text. Returns `None` if either the span is malformed,
/// or the source text is not accessible.
Expand Down Expand Up @@ -339,8 +345,11 @@ fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
.unwrap_or(sp)
}

/// A range within a specific [source file](SourceFile).
pub struct SourceFileRange {
/// The [source file](SourceFile) referred to by this range
pub sf: Arc<SourceFile>,
/// The range within the associated [source file](SourceFile)
pub range: Range<usize>,
}
impl SourceFileRange {
Expand Down Expand Up @@ -442,6 +451,11 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> {
// sources that the user has no control over.
// For some reason these attributes don't have any expansion info on them, so
// we have to check it this way until there is a better way.
//
/// Checks whether the code snippet referred to by the given [`Span`] is present in the source code.
///
/// For example, if the span refers to an attribute inserted during macro expansion, this will
/// return false.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this is accurate

pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
if let Some(snippet) = snippet_opt(sess, span)
&& snippet.is_empty()
Expand Down Expand Up @@ -631,6 +645,8 @@ pub fn snippet_block_with_applicability(
reindent_multiline(&snip, true, indent)
}

/// Same as [`snippet_block_with_applicability()`], but first walks the span up to the given context
/// using [`snippet_with_context()`].
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be more descriptive?

pub fn snippet_block_with_context(
sess: &impl HasSession,
span: Span,
Expand Down
8 changes: 8 additions & 0 deletions clippy_utils/src/str_utils.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
//! Utilities for analyzing and transforming strings.

/// Dealing with string indices can be hard, this struct ensures that both the
/// character and byte index are provided for correct indexing.
Copy link
Author

@SignalWalker SignalWalker Oct 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this mention that char_index and byte_index aren't guaranteed to agree with each other?

#[derive(Debug, Default, PartialEq, Eq)]
pub struct StrIndex {
/// The character-relative index within a string
pub char_index: usize,
/// The byte-relative index within a string
pub byte_index: usize,
}

impl StrIndex {
/// Creates a `StrIndex` from character and byte indices.
pub fn new(char_index: usize, byte_index: usize) -> Self {
Self { char_index, byte_index }
}
Expand Down Expand Up @@ -169,11 +174,14 @@ pub fn camel_case_split(s: &str) -> Vec<&str> {
/// character and byte count are provided for correct indexing.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this mention that char_count and byte_count aren't guaranteed to agree with each other?

#[derive(Debug, Default, PartialEq, Eq)]
pub struct StrCount {
/// The number of characters in a string.
pub char_count: usize,
/// The number of bytes in a string.
pub byte_count: usize,
}

impl StrCount {
/// Creates a `StrCount` from character and byte counts.
pub fn new(char_count: usize, byte_count: usize) -> Self {
Self { char_count, byte_count }
}
Expand Down
1 change: 1 addition & 0 deletions clippy_utils/src/sugg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ impl<'a> Sugg<'a> {
}
}

/// Convert this suggestion into a [`String`] to be displayed to the user.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming that this is this function's purpose based on how it's used elsewhere.

pub fn into_string(self) -> String {
match self {
Sugg::NonParen(p) | Sugg::MaybeParen(p) => p.into_owned(),
Expand Down
Loading