diff options
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | lib/Cargo.toml | 1 | ||||
| -rw-r--r-- | lib/src/lib.rs | 8 | ||||
| -rw-r--r-- | lib/src/lints.rs | 1 | ||||
| -rw-r--r-- | lib/src/lints/collapsible_let_in.rs | 60 | ||||
| -rw-r--r-- | lib/src/make.rs | 4 |
6 files changed, 71 insertions, 4 deletions
| @@ -64,6 +64,7 @@ dependencies = [ | |||
| 64 | "lazy_static", | 64 | "lazy_static", |
| 65 | "macros", | 65 | "macros", |
| 66 | "rnix", | 66 | "rnix", |
| 67 | "rowan", | ||
| 67 | ] | 68 | ] |
| 68 | 69 | ||
| 69 | [[package]] | 70 | [[package]] |
diff --git a/lib/Cargo.toml b/lib/Cargo.toml index e371c5d..e9e9e69 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml | |||
| @@ -10,3 +10,4 @@ rnix = "0.9.0" | |||
| 10 | if_chain = "1.0" | 10 | if_chain = "1.0" |
| 11 | macros = { path = "../macros" } | 11 | macros = { path = "../macros" } |
| 12 | lazy_static = "1.0" | 12 | lazy_static = "1.0" |
| 13 | rowan = "0.12.5" | ||
diff --git a/lib/src/lib.rs b/lib/src/lib.rs index f0f26dd..4065345 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs | |||
| @@ -18,7 +18,7 @@ pub struct Report { | |||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | impl Report { | 20 | impl Report { |
| 21 | /// Construct a report. Do not invoke manually, see `lint` macro | 21 | /// Construct a report. Do not invoke `Report::new` manually, see `lint` macro |
| 22 | pub fn new(note: &'static str, code: u32) -> Self { | 22 | pub fn new(note: &'static str, code: u32) -> Self { |
| 23 | Self { | 23 | Self { |
| 24 | note, | 24 | note, |
| @@ -27,7 +27,7 @@ impl Report { | |||
| 27 | } | 27 | } |
| 28 | } | 28 | } |
| 29 | /// Add a diagnostic to this report | 29 | /// Add a diagnostic to this report |
| 30 | pub fn diagnostic(mut self, at: TextRange, message: String) -> Self { | 30 | pub fn diagnostic<S: AsRef<str>>(mut self, at: TextRange, message: S) -> Self { |
| 31 | self.diagnostics.push(Diagnostic::new(at, message)); | 31 | self.diagnostics.push(Diagnostic::new(at, message)); |
| 32 | self | 32 | self |
| 33 | } | 33 | } |
| @@ -55,10 +55,10 @@ pub struct Diagnostic { | |||
| 55 | 55 | ||
| 56 | impl Diagnostic { | 56 | impl Diagnostic { |
| 57 | /// Construct a diagnostic. | 57 | /// Construct a diagnostic. |
| 58 | pub fn new(at: TextRange, message: String) -> Self { | 58 | pub fn new<S: AsRef<str>>(at: TextRange, message: S) -> Self { |
| 59 | Self { | 59 | Self { |
| 60 | at, | 60 | at, |
| 61 | message, | 61 | message: message.as_ref().into(), |
| 62 | suggestion: None, | 62 | suggestion: None, |
| 63 | } | 63 | } |
| 64 | } | 64 | } |
diff --git a/lib/src/lints.rs b/lib/src/lints.rs index 98aa404..7da4933 100644 --- a/lib/src/lints.rs +++ b/lib/src/lints.rs | |||
| @@ -6,4 +6,5 @@ lint_map! { | |||
| 6 | manual_inherit, | 6 | manual_inherit, |
| 7 | manual_inherit_from, | 7 | manual_inherit_from, |
| 8 | legacy_let_syntax, | 8 | legacy_let_syntax, |
| 9 | collapsible_let_in, | ||
| 9 | } | 10 | } |
diff --git a/lib/src/lints/collapsible_let_in.rs b/lib/src/lints/collapsible_let_in.rs new file mode 100644 index 0000000..c2cdf9b --- /dev/null +++ b/lib/src/lints/collapsible_let_in.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | ||
| 2 | |||
| 3 | use if_chain::if_chain; | ||
| 4 | use macros::lint; | ||
| 5 | use rowan::Direction; | ||
| 6 | use rnix::{ | ||
| 7 | types::{LetIn, TypedNode}, | ||
| 8 | NodeOrToken, SyntaxElement, SyntaxKind, TextRange | ||
| 9 | }; | ||
| 10 | |||
| 11 | #[lint( | ||
| 12 | name = "collapsible let in", | ||
| 13 | note = "These let-in expressions are collapsible", | ||
| 14 | code = 6, | ||
| 15 | match_with = SyntaxKind::NODE_LET_IN | ||
| 16 | )] | ||
| 17 | struct CollapsibleLetIn; | ||
| 18 | |||
| 19 | impl Rule for CollapsibleLetIn { | ||
| 20 | fn validate(&self, node: &SyntaxElement) -> Option<Report> { | ||
| 21 | if_chain! { | ||
| 22 | if let NodeOrToken::Node(node) = node; | ||
| 23 | if let Some(let_in_expr) = LetIn::cast(node.clone()); | ||
| 24 | if let Some(body) = let_in_expr.body(); | ||
| 25 | |||
| 26 | if LetIn::cast(body.clone()).is_some(); | ||
| 27 | then { | ||
| 28 | let first_annotation = node.text_range(); | ||
| 29 | let first_message = "This let-in expression contains a nested let-in expression"; | ||
| 30 | |||
| 31 | let second_annotation = body.text_range(); | ||
| 32 | let second_message = "This let-in expression is nested"; | ||
| 33 | |||
| 34 | let replacement_at = { | ||
| 35 | let start = body | ||
| 36 | .siblings_with_tokens(Direction::Prev) | ||
| 37 | .find(|elem| elem.kind() == SyntaxKind::TOKEN_IN)? | ||
| 38 | .text_range() | ||
| 39 | .start(); | ||
| 40 | let end = body | ||
| 41 | .descendants_with_tokens() | ||
| 42 | .find(|elem| elem.kind() == SyntaxKind::TOKEN_LET)? | ||
| 43 | .text_range() | ||
| 44 | .end(); | ||
| 45 | TextRange::new(start, end) | ||
| 46 | }; | ||
| 47 | let replacement = make::empty().node().clone(); | ||
| 48 | |||
| 49 | Some( | ||
| 50 | Self::report() | ||
| 51 | .diagnostic(first_annotation, first_message) | ||
| 52 | .suggest(second_annotation, second_message, Suggestion::new(replacement_at, replacement)) | ||
| 53 | ) | ||
| 54 | } else { | ||
| 55 | None | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
diff --git a/lib/src/make.rs b/lib/src/make.rs index 2a6450c..aec32d5 100644 --- a/lib/src/make.rs +++ b/lib/src/make.rs | |||
| @@ -75,3 +75,7 @@ pub fn select(set: &SyntaxNode, index: &SyntaxNode) -> types::Select { | |||
| 75 | pub fn ident(text: &str) -> types::Ident { | 75 | pub fn ident(text: &str) -> types::Ident { |
| 76 | ast_from_text(text) | 76 | ast_from_text(text) |
| 77 | } | 77 | } |
| 78 | |||
| 79 | pub fn empty() -> types::Root { | ||
| 80 | ast_from_text("") | ||
| 81 | } | ||
