Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
050ba80
`clippy_dev`: When parsing, keep each source file in memory.
Jarcho Oct 30, 2025
c972185
`clippy_dev`: Reuse already loaded files data when formatting.
Jarcho Nov 7, 2025
c01b0f8
`clippy_dev`: Reuse already loaded files when generating lint data.
Jarcho Nov 7, 2025
5cabbd6
`clippy_dev`: Don't store strings in token search patterns.
Jarcho Feb 7, 2026
0cdf9ce
`clippy_dev`: Remove `CaptureOptLifetimeArg` pattern.
Jarcho Feb 8, 2026
3857030
`clippy_dev`: Cleanup `Cursor` interface.
Jarcho Feb 8, 2026
6540420
`clippy_dev`: Move path parsing to it's own function.
Jarcho Feb 9, 2026
bb35953
`clippy_dev`: Store the span of each lint's name.
Jarcho Feb 12, 2026
52b6ba8
`clippy_dev`: Move some things from `parse` to `utils`.
Jarcho Feb 15, 2026
eca0167
`clippy_dev`: Use `annotate_snippets` to report all parsing errors to…
Jarcho Feb 9, 2026
d556475
`clippy_dev`: Move config parsing to the new parsing framework.
Jarcho Feb 16, 2026
963101f
`clippy_dev`: Panic immediately instead of returning errors in `add_l…
Jarcho Oct 20, 2025
54cd94f
`clippy_dev`: Parse more parts of `declare_clippy_lint` macro calls.
Jarcho Feb 26, 2026
32b311a
`clippy_dev`: Use the shared parsing logic in `new_lint`.
Jarcho Oct 30, 2025
08251e2
`clippy_dev`: Separate the IR defintions into a separate module.
Jarcho Mar 3, 2026
3e515ed
Make all lint pass modules public so registration can move into the d…
Jarcho Mar 3, 2026
9494d9c
`clippy_dev`: Parse lint pass constructors.
Jarcho Mar 1, 2026
bc91a9c
Automatically generate the lint pass registrations.
Jarcho Mar 3, 2026
3ef79f7
Move both the deprecated and active lints list into the root crate.
Jarcho Mar 23, 2026
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
14 changes: 12 additions & 2 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,12 @@ define_Conf! {
#[lints(inconsistent_struct_constructor)]
check_inconsistent_struct_field_initializers: bool = false,
/// Whether to also run the listed lints on private items.
#[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
#[lints(
missing_errors_doc,
missing_panics_doc,
missing_safety_doc,
unnecessary_safety_doc,
)]
check_private_items: bool = false,
/// The maximum cognitive complexity a function can have
#[lints(cognitive_complexity)]
Expand Down Expand Up @@ -684,7 +689,12 @@ define_Conf! {
#[lints(large_futures)]
future_size_threshold: u64 = 16 * 1024,
/// A list of paths to types that should be treated as if they do not contain interior mutability
#[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)]
#[lints(
borrow_interior_mutable_const,
declare_interior_mutable_const,
ifs_same_cond,
mutable_key_type,
)]
ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()]),
/// Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted.
#[lints(multiple_inherent_impl)]
Expand Down
4 changes: 3 additions & 1 deletion clippy_dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ version = "0.0.1"
edition = "2024"

[dependencies]
annotate-snippets = { version = "0.12.10", features = ["simd"] }
anstream = "0.6.20"
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
clap = { version = "4.4", features = ["derive"] }
indoc = "1.0"
itertools = "0.12"
memchr = "2.7.6"
opener = "0.7"
rustc-literal-escaper = "0.0.7"
walkdir = "2.3"
Expand Down
158 changes: 158 additions & 0 deletions clippy_dev/src/diag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use crate::Span;
use annotate_snippets::renderer::{DEFAULT_TERM_WIDTH, Renderer};
use annotate_snippets::{Annotation, AnnotationKind, Group, Level, Origin, Snippet};
use core::panic::Location;
use std::borrow::Cow;
use std::io::Write as _;
use std::process;

pub struct DiagCx {
out: anstream::Stderr,
renderer: Renderer,
has_err: bool,
}
impl Default for DiagCx {
fn default() -> Self {
let width = termize::dimensions().map_or(DEFAULT_TERM_WIDTH, |(w, _)| w);
Self {
out: anstream::stderr(),
renderer: Renderer::styled().term_width(width),
has_err: false,
}
}
}
impl Drop for DiagCx {
fn drop(&mut self) {
if self.has_err {
self.emit_err(&[
Group::with_title(
Level::ERROR
.with_name("internal error")
.primary_title("errors were found, but it was assumed none occurred"),
),
Group::with_title(Level::NOTE.secondary_title("any produced results may be incorrect")),
]);
process::exit(1);
}
}
}
impl DiagCx {
pub fn exit_on_err(&self) {
if self.has_err {
process::exit(1);
}
}

#[track_caller]
pub fn exit_assume_err(&mut self) -> ! {
if !self.has_err {
self.emit_err(&[
Group::with_title(
Level::ERROR
.with_name("internal error")
.primary_title("errors were expected, but is was assumed one would occur"),
),
mk_loc_group(Location::caller()),
]);
}
process::exit(1);
}
}

fn sp_to_snip(kind: AnnotationKind, sp: Span<'_>) -> Snippet<'_, Annotation<'_>> {
let line_starts = sp.file.line_starts();
let first_line = match line_starts.binary_search(&sp.range.start) {
Ok(x) => x,
// Note: `Err(0)` isn't possible since `0` is always the first start.
Err(x) => x - 1,
};
let start = line_starts[first_line] as usize;
let last_line = match line_starts.binary_search(&sp.range.end) {
Ok(x) => x,
Err(x) => x - 1,
};
let end = line_starts
.get(last_line + 1)
.map_or(sp.file.contents.len(), |&x| x as usize);
Snippet::source(&sp.file.contents[start..end])
.line_start(first_line + 1)
.path(sp.file.path.get())
.annotation(kind.span((sp.range.start as usize - start..sp.range.end as usize - start).into()))
}

fn mk_spanned_primary<'a>(level: Level<'a>, sp: Span<'a>, msg: impl Into<Cow<'a, str>>) -> Group<'a> {
level
.primary_title(msg.into())
.element(sp_to_snip(AnnotationKind::Primary, sp))
}

fn mk_spanned_secondary<'a>(level: Level<'a>, sp: Span<'a>, msg: impl Into<Cow<'a, str>>) -> Group<'a> {
level
.secondary_title(msg.into())
.element(sp_to_snip(AnnotationKind::Context, sp))
}

fn mk_loc_group<'a>(loc: &Location<'a>) -> Group<'a> {
Level::INFO.secondary_title("error created here").element(
Origin::path(loc.file())
.line(loc.line() as usize)
.char_column(loc.column() as usize),
)
}

impl DiagCx {
fn emit_err(&mut self, groups: &[Group<'_>]) {
let mut s = self.renderer.render(groups);
s.push('\n');
self.out.write_all(s.as_bytes()).unwrap();
self.has_err = true;
}

#[track_caller]
pub fn emit_spanned_err<'a>(&mut self, sp: Span<'a>, msg: impl Into<Cow<'a, str>>) {
self.emit_err(&[
mk_spanned_primary(Level::ERROR, sp, msg.into()),
mk_loc_group(Location::caller()),
]);
}

pub fn emit_spanned_err_loc<'a>(&mut self, sp: Span<'a>, msg: impl Into<Cow<'a, str>>, loc: &Location<'_>) {
self.emit_err(&[mk_spanned_primary(Level::ERROR, sp, msg.into()), mk_loc_group(loc)]);
}

#[track_caller]
pub fn emit_spanless_err<'a>(&mut self, msg: impl Into<Cow<'a, str>>) {
self.emit_err(&[
Group::with_title(Level::ERROR.primary_title(msg.into())),
mk_loc_group(Location::caller()),
]);
}

#[track_caller]
pub fn emit_already_deprecated(&mut self, name: &str) {
self.emit_spanless_err(format!("lint `{name}` is already deprecated"));
}

#[track_caller]
pub fn emit_duplicate_lint(&mut self, sp: Span<'_>, first_sp: Span<'_>) {
self.emit_err(&[
mk_spanned_primary(Level::ERROR, sp, "duplicate lint name declared"),
mk_spanned_secondary(Level::NOTE, first_sp, "previous declaration here"),
mk_loc_group(Location::caller()),
]);
}

#[track_caller]
pub fn emit_not_clippy_lint_name(&mut self, sp: Span<'_>) {
self.emit_err(&[
mk_spanned_primary(Level::ERROR, sp, "not a clippy lint name"),
Group::with_title(Level::HELP.secondary_title("add the `clippy::` tool prefix")),
mk_loc_group(Location::caller()),
]);
}

#[track_caller]
pub fn emit_unknown_lint(&mut self, name: &str) {
self.emit_spanless_err(format!("unknown lint `{name}`"));
}
}
Loading
Loading