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 | } | ||