aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--lib/Cargo.toml1
-rw-r--r--lib/src/lib.rs8
-rw-r--r--lib/src/lints.rs1
-rw-r--r--lib/src/lints/collapsible_let_in.rs60
-rw-r--r--lib/src/make.rs4
6 files changed, 71 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bae8674..8b371ea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
10if_chain = "1.0" 10if_chain = "1.0"
11macros = { path = "../macros" } 11macros = { path = "../macros" }
12lazy_static = "1.0" 12lazy_static = "1.0"
13rowan = "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
20impl Report { 20impl 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
56impl Diagnostic { 56impl 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion};
2
3use if_chain::if_chain;
4use macros::lint;
5use rowan::Direction;
6use 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)]
17struct CollapsibleLetIn;
18
19impl 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 {
75pub fn ident(text: &str) -> types::Ident { 75pub fn ident(text: &str) -> types::Ident {
76 ast_from_text(text) 76 ast_from_text(text)
77} 77}
78
79pub fn empty() -> types::Root {
80 ast_from_text("")
81}