mod lints; mod make; pub use lints::LINTS; use rnix::{SyntaxElement, SyntaxKind, TextRange}; use std::{convert::Into, default::Default}; /// Report generated by a lint #[derive(Debug, Default)] pub struct Report { /// General information about this lint and where it applies. pub note: &'static str, /// An error code to uniquely identify this lint pub code: u32, /// Collection of diagnostics raised by this lint pub diagnostics: Vec, } impl Report { /// Construct a report. Do not invoke `Report::new` manually, see `lint` macro pub fn new(note: &'static str, code: u32) -> Self { Self { note, code, ..Default::default() } } /// Add a diagnostic to this report pub fn diagnostic>(mut self, at: TextRange, message: S) -> Self { self.diagnostics.push(Diagnostic::new(at, message)); self } /// Add a diagnostic with a fix to this report pub fn suggest>( mut self, at: TextRange, message: S, suggestion: Suggestion, ) -> Self { self.diagnostics .push(Diagnostic::suggest(at, message, suggestion)); self } } /// Mapping from a bytespan to an error message. /// Can optionally suggest a fix. #[derive(Debug)] pub struct Diagnostic { pub at: TextRange, pub message: String, pub suggestion: Option, } impl Diagnostic { /// Construct a diagnostic. pub fn new>(at: TextRange, message: S) -> Self { Self { at, message: message.as_ref().into(), suggestion: None, } } /// Construct a diagnostic with a fix. pub fn suggest>(at: TextRange, message: S, suggestion: Suggestion) -> Self { Self { at, message: message.as_ref().into(), suggestion: Some(suggestion), } } } /// Suggested fix for a diagnostic, the fix is provided as a syntax element. /// Look at `make.rs` to construct fixes. #[derive(Debug)] pub struct Suggestion { pub at: TextRange, pub fix: SyntaxElement, } impl Suggestion { /// Construct a suggestion. pub fn new>(at: TextRange, fix: E) -> Self { Self { at, fix: fix.into(), } } } /// Lint logic is defined via this trait. Do not implement manually, /// look at the `lint` attribute macro instead for implementing rules pub trait Rule { fn validate(&self, node: &SyntaxElement) -> Option; } /// Contains information about the lint itself. Do not implement manually, /// look at the `lint` attribute macro instead for implementing rules pub trait Metadata { fn name() -> &'static str where Self: Sized; fn note() -> &'static str where Self: Sized; fn code() -> u32 where Self: Sized; fn report() -> Report where Self: Sized; fn match_with(&self, with: &SyntaxKind) -> bool; fn match_kind(&self) -> SyntaxKind; } /// Combines Rule and Metadata, do not implement manually, this is derived by /// the `lint` macro. pub trait Lint: Metadata + Rule + Send + Sync {} /// Helper utility to take lints from modules and insert them into a map for efficient /// access. Mapping is from a SyntaxKind to a list of lints that apply on that Kind. /// /// See `lints.rs` for usage. #[macro_export] macro_rules! lint_map { ($($s:ident),*,) => { lint_map!($($s),*); }; ($($s:ident),*) => { use ::std::collections::HashMap; use ::rnix::SyntaxKind; $( mod $s; )* ::lazy_static::lazy_static! { pub static ref LINTS: HashMap>> = { let mut map = HashMap::new(); $( { let temp_lint = &*$s::LINT; let temp_match = temp_lint.match_kind(); map.entry(temp_match) .and_modify(|v: &mut Vec<_>| v.push(temp_lint)) .or_insert_with(|| vec![temp_lint]); } )* map }; } } }