Skip to content

Commit e8709c7

Browse files
committed
Add lint/no-op-assignment
1 parent 95e2a64 commit e8709c7

File tree

8 files changed

+125
-1
lines changed

8 files changed

+125
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This changelog is loose. Versions are not semantic, they are incremental. Splint
1111
- `lint/no-catch`: Require `(try)` calls to have at least 1 `catch` (or `finally`) clause. Supports two styles: `:accept-finally` and `:only-catch`. `:accept-finally` will count a `finally` clause and not raise a warning, while `:only-catch` requires all `try` calls to have a `catch` clause.
1212
- `lint/catch-throwable`: Prefer specific Exceptions and Errors over `(catch Throwable t ...)`.
1313
- `lint/identical-branches`: Checks for identical branches of `if` and `cond` forms: `(if (pred) foo foo)`, `(cond (pred1) foo (pred2) foo)`. In `cond` branches, only checks consecutive branches as order of checks might be important otherwise.
14+
- `lint/no-op-assignment`: Avoid writing `(let [foo foo] ...)` or similar. No need to assign a variable to itself.
1415

1516
### Added
1617

docs/rules/lint.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [lint/dot-obj-method](#lintdot-obj-method)
1313
- [lint/duplicate-case-test](#lintduplicate-case-test)
1414
- [lint/duplicate-field-name](#lintduplicate-field-name)
15+
- [lint/existing-constant](#lintexisting-constant)
1516
- [lint/fn-wrapper](#lintfn-wrapper)
1617
- [lint/identical-branches](#lintidentical-branches)
1718
- [lint/if-else-nil](#lintif-else-nil)
@@ -30,6 +31,7 @@
3031
- [lint/misplaced-type-hint](#lintmisplaced-type-hint)
3132
- [lint/missing-body-in-when](#lintmissing-body-in-when)
3233
- [lint/no-catch](#lintno-catch)
34+
- [lint/no-op-assignment](#lintno-op-assignment)
3335
- [lint/not-empty?](#lintnot-empty)
3436
- [lint/prefer-method-values](#lintprefer-method-values)
3537
- [lint/prefer-require-over-use](#lintprefer-require-over-use)
@@ -288,6 +290,30 @@ with the same name, but it's good to catch these things early too.
288290

289291
---
290292

293+
## lint/existing-constant
294+
295+
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
296+
| ------------------ | ---- | ----------- | ------------- | --------------- |
297+
| true | true | false | <<next>> | <<next>> |
298+
299+
**NOTE:** Requires Clojure version 1.11.0.
300+
301+
Java has `PI` and `E` constants built-in, and `clojure.math` exposes them directly. Better to use them instead of poorly approximating them with vars.
302+
303+
### Examples
304+
305+
```clojure
306+
; avoid
307+
(def pi 3.14)
308+
(def e 2.718)
309+
310+
; prefer
311+
clojure.math/PI
312+
clojure.math/E
313+
```
314+
315+
---
316+
291317
## lint/fn-wrapper
292318

293319
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
@@ -757,6 +783,24 @@ With the default style `:accept-finally`, both `catch` and `finally` clauses are
757783

758784
---
759785

786+
## lint/no-op-assignment
787+
788+
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
789+
| ------------------ | ---- | ----------- | ------------- | --------------- |
790+
| true | true | false | <<next>> | <<next>> |
791+
792+
If the bind is a symbol and the expr is the same symbol, just use the expr directly. (Otherwise, indicates a potential bug.)
793+
794+
### Examples
795+
796+
```clojure
797+
; avoid
798+
(let [foo foo]
799+
...)
800+
```
801+
802+
---
803+
760804
## lint/not-empty?
761805

762806
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |

resources/config/default.edn

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@
201201
:chosen-style :accept-finally
202202
:supported-styles [:accept-finally :only-catch]}
203203

204+
lint/no-op-assignment
205+
{:description "Don't bind a symbol to the same symbol."
206+
:enabled true
207+
:added "<<next>>"
208+
:updated "<<next>>"}
209+
204210
lint/not-empty?
205211
{:description "Looks for `(not (empty?))` which matters for nil-punning."
206212
:enabled true

src/noahtheduke/splint.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
noahtheduke.splint.rules.lint.misplaced-type-hint
4747
noahtheduke.splint.rules.lint.missing-body-in-when
4848
noahtheduke.splint.rules.lint.no-catch
49+
noahtheduke.splint.rules.lint.no-op-assignment
4950
noahtheduke.splint.rules.lint.not-empty
5051
noahtheduke.splint.rules.lint.prefer-method-values
5152
noahtheduke.splint.rules.lint.prefer-require-over-use
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; This Source Code Form is subject to the terms of the Mozilla Public
2+
; License, v. 2.0. If a copy of the MPL was not distributed with this
3+
; file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
(ns ^:no-doc noahtheduke.splint.rules.lint.no-op-assignment
6+
(:require
7+
[noahtheduke.splint.diagnostic :refer [->diagnostic]]
8+
[noahtheduke.splint.rules :refer [defrule]]))
9+
10+
(set! *warn-on-reflection* true)
11+
12+
(defrule lint/no-op-assignment
13+
"If the bind is a symbol and the expr is the same symbol, just use the expr directly. (Otherwise, indicates a potential bug.)
14+
15+
@examples
16+
17+
; avoid
18+
(let [foo foo]
19+
...)
20+
"
21+
{:pattern '(let [?+bindings] ?*_)
22+
:on-match (fn [ctx rule form {:syms [?bindings]}]
23+
(when (even? (count ?bindings))
24+
(for [[bind expr] (partition 2 ?bindings)
25+
:when (and (symbol? bind)
26+
(= bind expr)
27+
(not (:splint/reader-cond (meta expr))))
28+
:let [new-form (with-meta (list bind expr)
29+
(meta bind))]]
30+
(->diagnostic ctx rule new-form {:message "Avoid no-op assignment."}))))})

test/noahtheduke/splint/clj_kondo_test.clj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
lint/misplaced-type-hint 10
3838
lint/missing-body-in-when 2
3939
lint/no-catch 1
40+
lint/no-op-assignment 2
4041
lint/prefer-require-over-use 4
4142
lint/redundant-str-call 18
4243
lint/thread-macro-one-arg 85
@@ -99,10 +100,11 @@
99100
diagnostics (delay (->> @results
100101
:diagnostics
101102
(group-by :rule-name)))]
103+
; (user/pprint (get @diagnostics 'lint/no-op-assignment))
102104
(it "has the right diagnostics"
103105
(expect
104106
(match?
105107
(m/equals clj-kondo-diagnostics)
106108
(update-vals* @diagnostics count))))
107109
(it "sums correctly"
108-
(expect (= 1363 (count (:diagnostics @results)))))))
110+
(expect (= 1365 (count (:diagnostics @results)))))))

test/noahtheduke/splint/netrunner_test.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
diagnostics (delay (->> @results
9595
:diagnostics
9696
(group-by :rule-name)))]
97+
; (user/pprint (get @diagnostics 'lint/no-op-assignment))
9798
(it "has the right diagnostics"
9899
(expect
99100
(match?
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
; This Source Code Form is subject to the terms of the Mozilla Public
2+
; License, v. 2.0. If a copy of the MPL was not distributed with this
3+
; file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
(ns noahtheduke.splint.rules.lint.no-op-assignment-test
6+
(:require
7+
[lazytest.core :refer [defdescribe it]]
8+
[noahtheduke.splint.test-helpers :refer [expect-match single-rule-config]]))
9+
10+
(set! *warn-on-reflection* true)
11+
12+
(def rule-name 'lint/no-op-assignment)
13+
14+
(defdescribe no-op-assignment-test
15+
(it "handles the trivial case"
16+
(expect-match
17+
[{:rule-name rule-name
18+
:form '(foo foo)
19+
:message "Avoid no-op assignment."
20+
:alt nil}]
21+
"(let [foo foo] bar)"
22+
(single-rule-config rule-name)))
23+
(it "checks multiple cases at once"
24+
(expect-match
25+
[{:rule-name rule-name
26+
:form '(bar bar)
27+
:message "Avoid no-op assignment."
28+
:alt nil}
29+
{:rule-name rule-name
30+
:form '(foo foo)
31+
:message "Avoid no-op assignment."
32+
:alt nil}]
33+
"(let [foo 1 foo foo bar foo bar bar] bar)"
34+
(single-rule-config rule-name)))
35+
(it "doesn't raise when in a reader conditional"
36+
(expect-match
37+
nil
38+
"(let [remote #?(:clj remote :cljs (foo bar))])"
39+
(single-rule-config rule-name))))

0 commit comments

Comments
 (0)