Skip to content
Open
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
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
3 changes: 3 additions & 0 deletions clippy_dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ 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
153 changes: 153 additions & 0 deletions clippy_dev/src/diag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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::Stdout,
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::stdout(),
renderer: Renderer::styled().term_width(width),
has_err: false,
}
}
}
impl Drop for DiagCx {
fn drop(&mut self) {
if self.has_err {
self.render(&[
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.render(&[
Group::with_title(
Level::ERROR
.with_name("internal error")
.primary_title("errors were expected, but is was assumed one would occur"),
),
mk_loc_group(),
]);
}
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))
}

#[track_caller]
fn mk_loc_group() -> Group<'static> {
let loc = Location::caller();
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 render(&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.render(&[mk_spanned_primary(Level::ERROR, sp, msg.into()), mk_loc_group()]);
}

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

#[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.render(&[
mk_spanned_primary(Level::ERROR, sp, "duplicate lint name declared"),
mk_spanned_secondary(Level::NOTE, first_sp, "previous declaration here"),
mk_loc_group(),
]);
}

#[track_caller]
pub fn emit_not_clippy_lint_name(&mut self, sp: Span<'_>) {
self.render(&[
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(),
]);
}

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