aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-09-21 16:21:20 +0100
committerAkshay <[email protected]>2021-09-21 16:21:20 +0100
commit7953d8cc9345794613ba2e7b90af9429579bc0cf (patch)
treebccaa3ba5623055c76cebd226efcca0ecd4db017
parent3eec886fe83b30636622d6ba97880bf473de8a0a (diff)
add error code and report codegen, document a bit
-rw-r--r--lib/src/lib.rs76
-rw-r--r--lib/src/lints/bool_comparison.rs6
-rw-r--r--macros/src/lib.rs29
3 files changed, 83 insertions, 28 deletions
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index 0f92d0d..93792d4 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -6,10 +6,41 @@ pub use lints::LINTS;
6use rnix::{SyntaxElement, SyntaxKind, TextRange}; 6use rnix::{SyntaxElement, SyntaxKind, TextRange};
7use std::{default::Default, convert::Into}; 7use std::{default::Default, convert::Into};
8 8
9pub trait Rule { 9/// Report generated by a lint
10 fn validate(&self, node: &SyntaxElement) -> Option<Report>; 10#[derive(Debug, Default)]
11pub struct Report {
12 /// General information about this lint and where it applies.
13 pub note: &'static str,
14 /// An error code to uniquely identify this lint
15 pub code: u32,
16 /// Collection of diagnostics raised by this lint
17 pub diagnostics: Vec<Diagnostic>,
18}
19
20impl Report {
21 /// Construct a report. Do not invoke manually, see `lint` macro
22 pub fn new(note: &'static str, code: u32) -> Self {
23 Self {
24 note,
25 code,
26 ..Default::default()
27 }
28 }
29 /// Add a diagnostic to this report
30 pub fn diagnostic(mut self, at: TextRange, message: String) -> Self {
31 self.diagnostics.push(Diagnostic::new(at, message));
32 self
33 }
34 /// Add a diagnostic with a fix to this report
35 pub fn suggest(mut self, at: TextRange, message: String, suggestion: Suggestion) -> Self {
36 self.diagnostics.push(Diagnostic::suggest(at, message, suggestion));
37 self
38 }
39
11} 40}
12 41
42/// Mapping from a bytespan to an error message.
43/// Can optionally suggest a fix.
13#[derive(Debug)] 44#[derive(Debug)]
14pub struct Diagnostic { 45pub struct Diagnostic {
15 pub at: TextRange, 46 pub at: TextRange,
@@ -18,14 +49,18 @@ pub struct Diagnostic {
18} 49}
19 50
20impl Diagnostic { 51impl Diagnostic {
52 /// Construct a diagnostic.
21 pub fn new(at: TextRange, message: String) -> Self { 53 pub fn new(at: TextRange, message: String) -> Self {
22 Self { at, message, suggestion: None } 54 Self { at, message, suggestion: None }
23 } 55 }
56 /// Construct a diagnostic with a fix.
24 pub fn suggest(at: TextRange, message: String, suggestion: Suggestion) -> Self { 57 pub fn suggest(at: TextRange, message: String, suggestion: Suggestion) -> Self {
25 Self { at, message, suggestion: Some(suggestion) } 58 Self { at, message, suggestion: Some(suggestion) }
26 } 59 }
27} 60}
28 61
62/// Suggested fix for a diagnostic, the fix is provided as a syntax element.
63/// Look at `make.rs` to construct fixes.
29#[derive(Debug)] 64#[derive(Debug)]
30pub struct Suggestion { 65pub struct Suggestion {
31 pub at: TextRange, 66 pub at: TextRange,
@@ -33,6 +68,7 @@ pub struct Suggestion {
33} 68}
34 69
35impl Suggestion { 70impl Suggestion {
71 /// Construct a suggestion.
36 pub fn new<E: Into<SyntaxElement>>(at: TextRange, fix: E) -> Self { 72 pub fn new<E: Into<SyntaxElement>>(at: TextRange, fix: E) -> Self {
37 Self { 73 Self {
38 at, 74 at,
@@ -41,39 +77,31 @@ impl Suggestion {
41 } 77 }
42} 78}
43 79
44#[derive(Debug, Default)] 80/// Lint logic is defined via this trait. Do not implement manually,
45pub struct Report { 81/// look at the `lint` attribute macro instead for implementing rules
46 pub diagnostics: Vec<Diagnostic>, 82pub trait Rule {
47 pub note: &'static str 83 fn validate(&self, node: &SyntaxElement) -> Option<Report>;
48}
49
50impl Report {
51 pub fn new(note: &'static str) -> Self {
52 Self {
53 note,
54 ..Default::default()
55 }
56 }
57 pub fn diagnostic(mut self, at: TextRange, message: String) -> Self {
58 self.diagnostics.push(Diagnostic::new(at, message));
59 self
60 }
61 pub fn suggest(mut self, at: TextRange, message: String, suggestion: Suggestion) -> Self {
62 self.diagnostics.push(Diagnostic::suggest(at, message, suggestion));
63 self
64 }
65
66} 84}
67 85
86/// Contains information about the lint itself. Do not implement manually,
87/// look at the `lint` attribute macro instead for implementing rules
68pub trait Metadata { 88pub trait Metadata {
69 fn name() -> &'static str where Self: Sized; 89 fn name() -> &'static str where Self: Sized;
70 fn note() -> &'static str where Self: Sized; 90 fn note() -> &'static str where Self: Sized;
91 fn code() -> u32 where Self: Sized;
92 fn report() -> Report where Self: Sized;
71 fn match_with(&self, with: &SyntaxKind) -> bool; 93 fn match_with(&self, with: &SyntaxKind) -> bool;
72 fn match_kind(&self) -> SyntaxKind; 94 fn match_kind(&self) -> SyntaxKind;
73} 95}
74 96
97/// Combines Rule and Metadata, do not implement manually, this is derived by
98/// the `lint` macro.
75pub trait Lint: Metadata + Rule + Send + Sync {} 99pub trait Lint: Metadata + Rule + Send + Sync {}
76 100
101/// Helper utility to take lints from modules and insert them into a map for efficient
102/// access. Mapping is from a SyntaxKind to a list of lints that apply on that Kind.
103///
104/// See `lints.rs` for usage.
77#[macro_export] 105#[macro_export]
78macro_rules! lint_map { 106macro_rules! lint_map {
79 ($($s:ident),*,) => { 107 ($($s:ident),*,) => {
diff --git a/lib/src/lints/bool_comparison.rs b/lib/src/lints/bool_comparison.rs
index f869e17..d23a4b0 100644
--- a/lib/src/lints/bool_comparison.rs
+++ b/lib/src/lints/bool_comparison.rs
@@ -10,6 +10,7 @@ use rnix::{
10#[lint( 10#[lint(
11 name = "bool_comparison", 11 name = "bool_comparison",
12 note = "Unnecessary comparison with boolean", 12 note = "Unnecessary comparison with boolean",
13 code = 1,
13 match_with = SyntaxKind::NODE_BIN_OP 14 match_with = SyntaxKind::NODE_BIN_OP
14)] 15)]
15struct BoolComparison; 16struct BoolComparison;
@@ -70,10 +71,7 @@ impl Rule for BoolComparison {
70 non_bool_side, 71 non_bool_side,
71 bool_side 72 bool_side
72 ); 73 );
73 Some( 74 Some(Self::report().suggest(at, message, Suggestion::new(at, replacement)))
74 Report::new(Self::note())
75 .suggest(at, message, Suggestion::new(at, replacement))
76 )
77 } else { 75 } else {
78 None 76 None
79 } 77 }
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 36a8bb5..c33fcba 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -53,12 +53,16 @@ fn generate_self_impl(struct_name: &Ident) -> TokenStream2 {
53fn generate_meta_impl(struct_name: &Ident, meta: &LintMeta) -> TokenStream2 { 53fn generate_meta_impl(struct_name: &Ident, meta: &LintMeta) -> TokenStream2 {
54 let name_fn = generate_name_fn(meta); 54 let name_fn = generate_name_fn(meta);
55 let note_fn = generate_note_fn(meta); 55 let note_fn = generate_note_fn(meta);
56 let code_fn = generate_code_fn(meta);
57 let report_fn = generate_report_fn();
56 let match_with_fn = generate_match_with_fn(meta); 58 let match_with_fn = generate_match_with_fn(meta);
57 let match_kind = generate_match_kind(meta); 59 let match_kind = generate_match_kind(meta);
58 quote! { 60 quote! {
59 impl Metadata for #struct_name { 61 impl Metadata for #struct_name {
60 #name_fn 62 #name_fn
61 #note_fn 63 #note_fn
64 #code_fn
65 #report_fn
62 #match_with_fn 66 #match_with_fn
63 #match_kind 67 #match_kind
64 } 68 }
@@ -99,6 +103,31 @@ fn generate_note_fn(meta: &LintMeta) -> TokenStream2 {
99 panic!("Invalid value for `note`"); 103 panic!("Invalid value for `note`");
100} 104}
101 105
106fn generate_code_fn(meta: &LintMeta) -> TokenStream2 {
107 let code = meta
108 .0
109 .get(&format_ident!("code"))
110 .unwrap_or_else(|| panic!("`code` not present"));
111 if let syn::Expr::Lit(code_lit) = code {
112 if let Lit::Int(code_int) = &code_lit.lit {
113 return quote! {
114 fn code() -> u32 {
115 #code_int
116 }
117 };
118 }
119 }
120 panic!("Invalid value for `note`");
121}
122
123fn generate_report_fn() -> TokenStream2 {
124 quote! {
125 fn report() -> Report {
126 Report::new(Self::note(), Self::code())
127 }
128 }
129}
130
102fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 { 131fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 {
103 let match_with_lit = meta 132 let match_with_lit = meta
104 .0 133 .0