├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── cli.rkt ├── elisp ├── Cask ├── Makefile └── flycheck-racket-review.el ├── ext.rkt ├── info.rkt ├── lint.rkt ├── media └── screenshot.png ├── problem.rkt ├── run-tests.rkt └── tests └── lint ├── already-defined-argument.rkt ├── already-defined-argument.rkt.out ├── already-defined-let.rkt ├── already-defined-let.rkt.out ├── already-defined-values.rkt ├── already-defined-values.rkt.out ├── already-defined.rkt ├── already-defined.rkt.out ├── bad-brackets.rkt ├── bad-brackets.rkt.out ├── begin-inside-if.rkt ├── begin-inside-if.rkt.out ├── binding-else-in-match.rkt ├── binding-else-in-match.rkt.out ├── case-lamba.rkt ├── case-lamba.rkt.out ├── case.rkt ├── case.rkt.out ├── component-lib-system.rkt ├── component-lib-system.rkt.out ├── cond-bad-brackets.rkt ├── cond-bad-brackets.rkt.out ├── cond-without-else.rkt ├── cond-without-else.rkt.out ├── contracts.rkt ├── contracts.rkt.out ├── define-generics.rkt ├── define-generics.rkt.out ├── define-logger.rkt ├── define-logger.rkt.out ├── define-match.rkt ├── define-match.rkt.out ├── define-syntax-rule.rkt ├── define-syntax-rule.rkt.out ├── define-values-shadowing.rkt ├── define-values-shadowing.rkt.out ├── empty-let.rkt ├── empty-let.rkt.out ├── empty.rkt ├── empty.rkt.out ├── for-expressions.rkt ├── for-expressions.rkt.out ├── for-fold-clause-scope.rkt ├── for-fold-clause-scope.rkt.out ├── function-varargs.rkt ├── function-varargs.rkt.out ├── if-without-else.rkt ├── if-without-else.rkt.out ├── ignore-line.rkt ├── ignore-line.rkt.out ├── imports.rkt ├── imports.rkt.out ├── invalid-base-lang.rkt ├── invalid-base-lang.rkt.out ├── lambda-scope.rkt ├── lambda-scope.rkt.out ├── lambda-varargs.rkt ├── lambda-varargs.rkt.out ├── let-can-shadow.rkt ├── let-can-shadow.rkt.out ├── let-with-parens.rkt ├── let-with-parens.rkt.out ├── match-define.rkt ├── match-define.rkt.out ├── match-lambda.rkt ├── match-lambda.rkt.out ├── match.rkt ├── match.rkt.out ├── nested-modules.rkt ├── nested-modules.rkt.out ├── no-lang.rkt ├── no-lang.rkt.out ├── partial-lang.rkt ├── partial-lang.rkt.out ├── provided-all-defined.rkt ├── provided-all-defined.rkt.out ├── provided-but-not-defined.rkt ├── provided-but-not-defined.rkt.out ├── punted-bindings.rkt ├── punted-bindings.rkt.out ├── rackunit-test-case.rkt ├── rackunit-test-case.rkt.out ├── require-combine-in.rkt ├── require-combine-in.rkt.out ├── require-sorted-local.rkt ├── require-sorted-local.rkt.out ├── require-sorted.rkt ├── require-sorted.rkt.out ├── require-submod.rkt ├── require-submod.rkt.out ├── require-unsorted-for-syntax.rkt ├── require-unsorted-for-syntax.rkt.out ├── require-unsorted-local-1.rkt ├── require-unsorted-local-1.rkt.out ├── require-unsorted-local-2.rkt ├── require-unsorted-local-2.rkt.out ├── require-unsorted-prefix-in.rkt ├── require-unsorted-prefix-in.rkt.out ├── require-unsorted.rkt ├── require-unsorted.rkt.out ├── shadow-arg.rkt ├── shadow-arg.rkt.out ├── struct-fields.rkt ├── struct-fields.rkt.out ├── struct-methods.rkt ├── struct-methods.rkt.out ├── struct-mutable-field.rkt ├── struct-mutable-field.rkt.out ├── struct-mutable.rkt ├── struct-mutable.rkt.out ├── struct-out.rkt ├── struct-out.rkt.out ├── struct-plus-plus.rkt ├── struct-plus-plus.rkt.out ├── struct.rkt ├── struct.rkt.out ├── submodule.rkt ├── submodule.rkt.out ├── syntax-error.rkt ├── syntax-error.rkt.out ├── tilde-ids.rkt ├── tilde-ids.rkt.out ├── underscores-dont-shadow.rkt ├── underscores-dont-shadow.rkt.out ├── unused-bindings.rkt ├── unused-bindings.rkt.out ├── use-before-define.rkt ├── use-before-define.rkt.out ├── when-unless.rkt └── when-unless.rkt.out /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | run: 5 | name: 'CI' 6 | runs-on: 'ubuntu-latest' 7 | steps: 8 | - uses: actions/checkout@master 9 | - uses: Bogdanp/setup-racket@v1.11 10 | with: 11 | variant: 'CS' 12 | version: '8.11.1' 13 | - run: raco pkg install --name review 14 | - run: racket run-tests.rkt 15 | env: 16 | BATCH: x 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compiled 2 | doc 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019-2024 Bogdan Popa 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # racket-review 2 | 3 |

4 | GitHub Actions status 5 |

6 | 7 | ![a screenshot of racket-review being used inside Emacs](media/screenshot.png) 8 | 9 | `racket-review` performs surface-level linting of individual Racket 10 | modules with the intent of finding issues as quickly as it can. It 11 | does not expand the programs it lints, so there may be cases where 12 | it is wrong, but, in practice, it performs reasonably well on `#lang 13 | racket{,/base}` programs. 14 | 15 | It currently reports the following issues: 16 | 17 | * `[error]` "identifier is already defined" 18 | * `[error]` "if expressions must contain one expression for the then-branch and another for the else-branch" 19 | * `[error]` "let forms must contain at least one body expression" 20 | * `[error]` "syntax error" 21 | * `[error]` "use _ instead of else in the fallthrough case of a match expression" 22 | * `[error]` "case clause must be in the form ( ...), not '" 23 | * `[error]` "use '() for match pattern instead of null or empty" 24 | * `[warning]` "bindings within a let should be surrounded by square brackets" 25 | * `[warning]` "identifier * provided but not defined" 26 | * `[warning]` "identifier * is already defined" 27 | * `[warning]` "identifier * is never used" 28 | * `[warning]` "identifier * shadows an earlier binding" 29 | * `[warning]` "missing module (#lang) declaration" 30 | * `[warning]` "require (for-syntax ...) should come before all others" 31 | * `[warning]` "require * should come after *" 32 | * `[warning]` "require * should come before *" 33 | * `[warning]` "this cond expression does not have an else clause" 34 | * `[warning]` "use a cond expression instead of nesting begin or let inside an if" 35 | 36 | Various packages provide [extensions] to these rules. 37 | 38 | ## Setup 39 | 40 | $ raco pkg install review 41 | 42 | ## Usage 43 | 44 | $ raco review filename.rkt 45 | 46 | To tell the linter to ignore an entire module, add a comment like 47 | 48 | ``` racket 49 | #|review: ignore|# 50 | ``` 51 | 52 | to the source file. To tell it to ignore a particular line, end that 53 | line with one of the following comments: 54 | 55 | ``` racket 56 | ;; noqa 57 | ;; lint: ignore 58 | ;; review: ignore 59 | ``` 60 | 61 | ## Adding Custom Rules 62 | 63 | A package may declare its own linting rules by providing a `review-exts` 64 | definition in its top-level `info.rkt` file. Each `review-exts` 65 | definition is a list of triples, where the first value is an absolute 66 | module path, the second is the name of a predicate procedure provided by 67 | that module, and the third is the name of a linting procedure provided 68 | by that module. 69 | 70 | Both the predicate and the linting procedure must take a single syntax 71 | object as argument. The linting procedure is called on a piece of syntax 72 | whenever the predicate procedure returns `#t`. 73 | 74 | For an example, see [this package][ext example] and [this blog 75 | post][ext blog post]. 76 | 77 | ## Emacs/flycheck support 78 | 79 | Add the following snippet to your `init.el` to define a Flycheck 80 | checker for `racket-review`: 81 | 82 | ``` emacs-lisp 83 | (flycheck-define-checker racket-review 84 | "check racket source code using racket-review" 85 | :command ("raco" "review" source) 86 | :error-patterns 87 | ((error line-start (file-name) ":" line ":" column ":error:" (message) line-end) 88 | (warning line-start (file-name) ":" line ":" column ":warning:" (message) line-end)) 89 | :modes racket-mode) 90 | 91 | (add-to-list 'flycheck-checkers 'racket-review) 92 | ``` 93 | 94 | Or install the Emacs plugin from the `elisp` directory: 95 | 96 | ```shell 97 | (cd elisp && make install) 98 | ``` 99 | 100 | ## Prior work 101 | 102 | * http://planet.racket-lang.org/package-source/clements/no-brainer.plt/1/5/ 103 | * http://tmp.barzilay.org/code-ayatollah.rkt 104 | * https://github.com/jackfirth/syntax-warn 105 | 106 | ## License 107 | 108 | racket-review is licensed under the 3-Clause BSD license. 109 | 110 | [ext example]: https://github.com/Bogdanp/Noise/tree/48cd8359d49ca586b97832fc76fce505b9ec6364/Racket/noise-serde-lint-lib 111 | [ext blog post]: https://defn.io/2024/01/07/extensible-racket-linting/ 112 | [extensions]: https://pkgd.racket-lang.org/pkgn/search?tags=linter 113 | -------------------------------------------------------------------------------- /cli.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket/base 2 | 3 | (require racket/cmdline 4 | racket/format 5 | raco/command-name 6 | "lint.rkt" 7 | "problem.rkt") 8 | 9 | (define (report-problem p) 10 | (define-values (source line column) 11 | (problem-loc p)) 12 | 13 | (displayln (~a source ":" line ":" (add1 column) ":" (problem-level p) ":" (problem-message p)))) 14 | 15 | (define filename 16 | (command-line 17 | #:program (short-program+command-name) 18 | #:args (filename) 19 | (path->complete-path filename))) 20 | 21 | (define problems (lint filename)) 22 | (unless (null? problems) 23 | (for ([p (in-list (sort problems problem 5 | ;; Version: 0.0.0 6 | ;; Package-Requires: ((emacs "24.1") (flycheck "32")) 7 | ;; Homepage: https://github.com/Bogdanp/racket-review 8 | ;; Keywords: convenience processes tools 9 | ;; SPDX-License-Identifier: BSD-3-Clause 10 | 11 | 12 | 13 | ;;; Commentary: 14 | 15 | 16 | ;; Flycheck checker for Racket source code using racket-review. 17 | 18 | 19 | 20 | ;;; Code: 21 | 22 | 23 | (require 'flycheck) 24 | 25 | 26 | ;;;###autoload 27 | (flycheck-define-checker racket-review 28 | "Flycheck checker for Racket source code using racket-review." 29 | :modes racket-mode 30 | :command ("raco" "review" source) 31 | :error-patterns 32 | ((error 33 | line-start (file-name) ":" line ":" column ":error:" (message) line-end) 34 | (warning 35 | line-start (file-name) ":" line ":" column ":warning:" (message) line-end))) 36 | 37 | ;;;###autoload 38 | (add-to-list 'flycheck-checkers 'racket-review) 39 | 40 | 41 | (provide 'flycheck-racket-review) 42 | 43 | 44 | 45 | ;;; flycheck-racket-review.el ends here 46 | -------------------------------------------------------------------------------- /ext.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require (for-syntax racket/base) 4 | syntax/parse/pre) 5 | 6 | ;; indirection ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | (provide 9 | current-reviewer) 10 | 11 | (struct reviewer 12 | (recur-proc 13 | track-error-proc 14 | track-warning-proc 15 | track-binding-proc 16 | track-struct-usage-proc 17 | push-scope-proc 18 | pop-scope-proc)) 19 | 20 | (define current-reviewer 21 | (make-parameter #f)) 22 | 23 | (define-syntax (define-reviewer-procs stx) 24 | (syntax-case stx () 25 | [(_ {id accessor} ...) 26 | #'(begin 27 | (provide id ...) 28 | (define id 29 | (make-keyword-procedure 30 | (lambda (kws kw-args . args) 31 | (keyword-apply 32 | (accessor (current-reviewer)) 33 | kws kw-args args)))) 34 | ...)])) 35 | 36 | (define-reviewer-procs 37 | {recur reviewer-recur-proc} 38 | {track-error reviewer-track-error-proc} 39 | {track-warning reviewer-track-warning-proc} 40 | {track-binding reviewer-track-binding-proc} 41 | {track-struct-usage reviewer-track-struct-usage-proc} 42 | {push-scope reviewer-push-scope-proc} 43 | {pop-scope reviewer-pop-scope-proc}) 44 | 45 | (module+ private 46 | (provide (struct-out reviewer))) 47 | 48 | 49 | ;; syntax classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 50 | 51 | (provide 52 | expression) 53 | 54 | (define-syntax-class expression 55 | (pattern e #:do [(recur this-syntax)])) 56 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define license 'BSD-3-Clause) 4 | (define version "0.2") 5 | (define collection "review") 6 | 7 | (define deps '("base")) 8 | (define build-deps '("base" 9 | "at-exp-lib")) 10 | 11 | (define compile-omit-paths '("tests/lint")) 12 | (define test-omit-paths 'all) 13 | 14 | (define raco-commands 15 | '(("review" review/cli "run the review utility" #f))) 16 | -------------------------------------------------------------------------------- /lint.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | #|review: ignore|# 4 | 5 | (require racket/contract/base 6 | racket/contract/region 7 | racket/format 8 | racket/function 9 | racket/list 10 | racket/match 11 | racket/runtime-path 12 | racket/string 13 | racket/syntax 14 | setup/getinfo 15 | syntax/modread 16 | syntax/parse/pre 17 | (submod "ext.rkt" private) 18 | (only-in "ext.rkt" current-reviewer) 19 | "problem.rkt") 20 | 21 | (provide 22 | lint) 23 | 24 | (define ignore-all-re #rx"#\\|review: ignore\\|#") 25 | (define ignore-line-re #rx";; (noqa|lint: ignore|review: ignore)$") 26 | (define (lines-to-ignore filename) 27 | (call-with-input-file filename 28 | (lambda (in) 29 | (for/fold ([lines null]) 30 | ([(line idx) (in-indexed (in-lines in))]) 31 | (define all? (regexp-match? ignore-all-re line)) 32 | #:final all? 33 | (cond 34 | [all? 'all] 35 | [(regexp-match? ignore-line-re line) 36 | (cons (add1 idx) lines)] 37 | [else lines]))))) 38 | 39 | (define/contract (lint filename) 40 | (-> path-string? (listof problem?)) 41 | (define the-lines-to-ignore 42 | (lines-to-ignore filename)) 43 | (with-handlers ([exn:fail? 44 | (lambda (e) 45 | (track-error! (datum->syntax #f #f (list filename 1 0 1 1)) 46 | (car (string-split (exn-message e) "\n"))))]) 47 | (define stx (file->syntax filename)) 48 | (when (and stx 49 | (not (eof-object? stx)) 50 | (not (eq? 'all the-lines-to-ignore))) 51 | (lint-syntax! stx))) 52 | (define the-problems 53 | (remove-duplicates 54 | (current-problem-list))) 55 | (if (pair? the-lines-to-ignore) 56 | (filter-not 57 | (λ (p) 58 | (define-values (_source line _col) 59 | (problem-loc p)) 60 | (memv line the-lines-to-ignore)) 61 | the-problems) 62 | the-problems)) 63 | 64 | (define (track-problem! stx message [level 'warning]) 65 | (current-problem-list 66 | (cons 67 | (problem stx level message) 68 | (current-problem-list)))) 69 | 70 | (define (track-warning! stx message) 71 | (track-problem! stx message)) 72 | 73 | (define (track-error! stx message) 74 | (track-problem! stx message 'error)) 75 | 76 | (define (file->syntax filename) 77 | (with-handlers ([exn:fail:read? 78 | (lambda (e) 79 | (begin0 #f 80 | (for ([loc (exn:fail:read-srclocs e)]) 81 | (track-error! loc (cadr (string-split (exn-message e) "read-syntax: "))))))]) 82 | (define-values (base _ __) (split-path filename)) 83 | (parameterize ([current-load-relative-directory base] 84 | [current-namespace (make-base-namespace)]) 85 | (with-module-reading-parameterization 86 | (lambda () 87 | (call-with-input-file filename 88 | (lambda (in) 89 | (port-count-lines! in) 90 | (read-syntax filename in)))))))) 91 | 92 | (define (lint-syntax! stx) 93 | (syntax-parse stx 94 | [module:module 95 | (check-provided-bindings!) 96 | #'module] 97 | 98 | [e 99 | (track-warning! stx "missing module (#lang) declaration") 100 | #'e])) 101 | 102 | (define (lint-submodule-syntax! stx) 103 | (syntax-parse stx 104 | #:datum-literals (module) 105 | [(module ~! _name _path _e:toplevel ...) 106 | (check-provided-bindings!) 107 | #'module])) 108 | 109 | (define-runtime-module-path-index problem.rkt 110 | "problem.rkt") 111 | 112 | ;; Lint submodules in a fresh instance of this module, that way we don't 113 | ;; have to spend any code stacking states around. The instances share 114 | ;; problem.rkt, so the problem list itself is shared. 115 | (define (lint-submodule-syntax!/trampoline stx) 116 | (define ns (current-namespace)) 117 | (parameterize ([current-namespace (make-base-empty-namespace)]) 118 | (namespace-attach-module ns (module-path-index-resolve problem.rkt)) 119 | ((dynamic-require '(submod review/lint private) 'lint-submodule-syntax!) stx))) 120 | 121 | (module+ private 122 | (provide lint-submodule-syntax!)) 123 | 124 | 125 | ;; scope ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 126 | 127 | (define *scope-id-seq* 0) 128 | 129 | (define (next-scope-id!) 130 | (begin0 *scope-id-seq* 131 | (set! *scope-id-seq* (add1 *scope-id-seq*)))) 132 | 133 | (struct scope (id parent bindings) 134 | #:transparent) 135 | 136 | (define (make-scope parent bindings) 137 | (scope (next-scope-id!) parent bindings)) 138 | 139 | (define (scope-copy s) 140 | (scope (scope-id s) 141 | (scope-parent s) 142 | (hash-copy (scope-bindings s)))) 143 | 144 | (define (scope-descendant? s other-s) 145 | (let loop ([s* (scope-parent s)]) 146 | (cond 147 | [(and s* (= (scope-id s*) (scope-id other-s))) #t] 148 | [(and s* (scope-parent s*)) => loop] 149 | [else #f]))) 150 | 151 | (struct binding-info (stx usages check-usages? [related-stxes #:mutable]) 152 | #:transparent) 153 | 154 | (define (make-binding-info stx [usages 0] [check-usages? #t] [related-stxes null]) 155 | (binding-info stx usages check-usages? related-stxes)) 156 | 157 | (define (add-related-stx! bi stx) 158 | (set-binding-info-related-stxes! bi (cons stx (binding-info-related-stxes bi)))) 159 | 160 | (define current-scope 161 | (make-parameter (make-scope #f (make-hash)))) 162 | 163 | (define current-stashed-scope 164 | (make-parameter #f)) 165 | 166 | (define current-punted-bindings ;; name -> (listof scope) 167 | (make-parameter (hash))) 168 | 169 | (define (push-scope!) 170 | (current-scope (make-scope (current-scope) (make-hash)))) 171 | 172 | (define (pop-scope! [check-bindings? #t]) 173 | (when check-bindings? 174 | (check-unused-bindings!)) 175 | (current-scope (scope-parent (current-scope)))) 176 | 177 | (define (stash-scope!) 178 | (current-stashed-scope (current-scope)) 179 | (pop-scope! #f)) 180 | 181 | (define (restore-scope!) 182 | (unless (current-stashed-scope) 183 | (error 'restore-scope! "no stashed scope")) 184 | (current-scope (current-stashed-scope)) 185 | (current-stashed-scope #f)) 186 | 187 | (define (bindings-ref name) 188 | (let loop ([scope (current-scope)]) 189 | (cond 190 | [(hash-ref (scope-bindings scope) name #f)] 191 | [(scope-parent scope) => loop] 192 | [else #f]))) 193 | 194 | (define (name-bound? name) 195 | (and (bindings-ref name) #t)) 196 | 197 | (define (underscores? name) 198 | (define name:str (cond 199 | [(symbol? name) (symbol->string name)] 200 | [else name])) 201 | (string-prefix? name:str "_")) 202 | 203 | (define (name-bound-in-current-scope? name) 204 | (hash-has-key? (scope-bindings (current-scope)) name)) 205 | 206 | (define (track-binding! stx [fmt "~a"] 207 | #:check-usages? [check-usages? #t] 208 | #:related-to [related-to-stx #f]) 209 | (define name (format-binding fmt stx)) 210 | (define usages 211 | (cond 212 | [(binding-punted? name) 213 | (unpunt-binding! name) 214 | 1] 215 | 216 | [else 0])) 217 | 218 | (when related-to-stx 219 | (define related-id 220 | (format-binding "~a" related-to-stx)) 221 | (cond 222 | [(related-provided? related-id) 223 | (track-provided! (datum->syntax stx name))] 224 | 225 | [(bindings-ref related-id) 226 | => (lambda (bi) 227 | (add-related-stx! bi (datum->syntax stx name)))])) 228 | 229 | (hash-set! (scope-bindings (current-scope)) name (make-binding-info stx usages check-usages?))) 230 | 231 | (define (punt-binding! name) 232 | (current-punted-bindings 233 | (hash-update (current-punted-bindings) name (curry cons (current-scope)) null))) 234 | 235 | (define (unpunt-binding! name) 236 | (current-punted-bindings 237 | (hash-update (current-punted-bindings) name (curry remove (current-scope)) null))) 238 | 239 | (define (binding-punted? name) 240 | (cond 241 | [(hash-ref (current-punted-bindings) name #f) 242 | => (lambda (scopes) 243 | (for/first ([scope (in-list scopes)] 244 | #:when (scope-descendant? scope (current-scope))) 245 | #t))] 246 | 247 | [else #f])) 248 | 249 | (define (track-binding-usage! name) 250 | (let loop ([scope (current-scope)]) 251 | (define bindings (scope-bindings scope)) 252 | (cond 253 | [(hash-has-key? bindings name) 254 | (hash-update! bindings name 255 | (lambda (info) 256 | (struct-copy binding-info info [usages (add1 (binding-info-usages info))])))] 257 | 258 | [(scope-parent scope) => loop] 259 | [else (punt-binding! name)]))) 260 | 261 | (define (check-unused-bindings!) 262 | (for ([(name binding) (in-hash (scope-bindings (current-scope)))]) 263 | (when (and (binding-info-check-usages? binding) 264 | (= (binding-info-usages binding) 0) 265 | (not (binding-provided? name)) 266 | (not (underscores? name))) 267 | (track-warning! (binding-info-stx binding) (format "identifier '~a' is never used" name))))) 268 | 269 | 270 | ;; undo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 271 | 272 | (struct savepoint (scope punts problems) 273 | #:transparent) 274 | 275 | (define current-savepoint 276 | (make-parameter #f)) 277 | 278 | (define (save!) 279 | (current-savepoint 280 | (savepoint (scope-copy (current-scope)) 281 | (current-punted-bindings) 282 | (current-problem-list)))) 283 | 284 | (define (undo!) 285 | (current-scope (savepoint-scope (current-savepoint))) 286 | (current-punted-bindings (savepoint-punts (current-savepoint))) 287 | (current-problem-list (savepoint-problems (current-savepoint)))) 288 | 289 | 290 | ;; provide ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 291 | 292 | (define provided-bindings 293 | (make-parameter null)) 294 | 295 | (define related-provided 296 | (make-parameter null)) 297 | 298 | (define all-bindings-provided? 299 | (make-parameter #f)) 300 | 301 | (define (track-provided! name) 302 | (provided-bindings (cons name (provided-bindings)))) 303 | 304 | (define (track-related-provided! name) 305 | (related-provided (cons name (related-provided)))) 306 | 307 | (define (track-all-provided!) 308 | (all-bindings-provided? #t)) 309 | 310 | (define (check-provided-bindings!) 311 | (for ([binding:stx (provided-bindings)]) 312 | (define binding:id (syntax->datum binding:stx)) 313 | (unless (name-bound? binding:id) 314 | (track-warning! binding:stx (~a "identifier '" binding:id "' provided but not defined"))))) 315 | 316 | (define (binding-provided? name) 317 | (or (all-bindings-provided?) 318 | (for/first ([binding:stx (provided-bindings)] 319 | #:when (eq? name (syntax->datum binding:stx))) 320 | #t))) 321 | 322 | (define (related-provided? name) 323 | (for/first ([binding:stx (related-provided)] 324 | #:when (eq? name (syntax->datum binding:stx))) 325 | #t)) 326 | 327 | 328 | ;; rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 329 | 330 | (define (format-binding fmt . args) 331 | (define args:strs 332 | (for/list ([arg (in-list args)]) 333 | (cond 334 | [(symbol? arg) (symbol->string arg)] 335 | [(syntax? arg) (symbol->string (syntax->datum arg))] 336 | [else arg]))) 337 | 338 | (string->symbol (apply format fmt args:strs))) 339 | 340 | (define-syntax-class contract-arrow-dom-expression 341 | (pattern (rator:expression ~! rand:expression ...) 342 | #:do [(when (syntax-property this-syntax 'paren-shape) 343 | (unless (eq? (syntax-property this-syntax 'paren-shape) #\() 344 | (track-warning! this-syntax "contract domain expressions should use round parens; did you mean to use ->*?")))]) 345 | (pattern expr:expression)) 346 | 347 | (define-splicing-syntax-class contract-arrow-dom 348 | (pattern {~seq kwd:keyword expr:contract-arrow-dom-expression}) 349 | (pattern expr:contract-arrow-dom-expression)) 350 | 351 | (define-syntax-class contract-arrow-mandatory-dom 352 | (pattern [dom:contract-arrow-dom ...] 353 | #:do [(unless (eq? (syntax-property this-syntax 'paren-shape) #\[) 354 | (track-warning! this-syntax "mandatory domain parens should be square"))])) 355 | 356 | (define-syntax-class contract-arrow-optional-dom 357 | (pattern [dom:contract-arrow-dom ...] 358 | #:do [(unless (eq? (syntax-property this-syntax 'paren-shape) #\[) 359 | (track-warning! this-syntax "optional domain parens should be square"))] )) 360 | 361 | (define-syntax-class contract-arrow-range 362 | #:datum-literals (any values) 363 | (pattern any) 364 | (pattern (values ~! e:expression ...)) 365 | (pattern e:expression)) 366 | 367 | (define-syntax-class contract-arrow-expression 368 | #:datum-literals (-> ->*) 369 | (pattern (-> dom:contract-arrow-dom ... range-expr:contract-arrow-range)) 370 | (pattern (-> dom:contract-arrow-dom ... {~datum ...} dom-expr:contract-arrow-dom-expression ... range-expr:contract-arrow-range)) 371 | (pattern (->* mandatory-dom:contract-arrow-mandatory-dom 372 | {~optional optional-dom:contract-arrow-optional-dom} 373 | {~optional {~seq #:rest rest-expr:expression}} 374 | {~optional {~or {~seq #:pre pre-cond-expr:expression} 375 | {~seq #:pre/desc pre-cond-expr:expression}}} 376 | range:contract-arrow-range 377 | {~optional {~or {~seq #:post post-cond-expr:expression} 378 | {~seq #:post/desc post-cond-expr:expression}}}))) 379 | 380 | (define-syntax-class application-expression 381 | (pattern (e0:expression ~! e:expression ...))) 382 | 383 | (define-syntax-class identifier-expression 384 | (pattern id:id 385 | #:do [(track-binding-usage! (format-binding "~a" #'id)) 386 | (when (eq? (syntax->datum #'id) 'false/c) 387 | (track-warning! this-syntax "prefer #f over false/c"))])) 388 | 389 | (define-syntax-class lambda-expression 390 | #:datum-literals (case-lambda lambda λ) 391 | (pattern ((~or lambda λ) args:define-identifier/new-scope 392 | e0:expression ...+ 393 | (~do (pop-scope!)))) 394 | 395 | (pattern ((~or lambda λ) 396 | (~do (push-scope!)) 397 | (~or 398 | (~and 399 | (~do (save!)) 400 | ((~seq (~optional k:keyword) arg:function-argument) ...)) 401 | (~and 402 | (~do (undo!)) 403 | ((~seq (~optional k:keyword) arg:function-argument) ... . vararg:function-argument))) 404 | e1:expression ...+ 405 | (~do (pop-scope!)))) 406 | 407 | (pattern (case-lambda 408 | [{~do (push-scope!)} 409 | {~or 410 | {~and 411 | {~do (save!)} 412 | (arg:define-identifier ...)} 413 | {~and 414 | {~do (undo!)} 415 | {~do (save!)} 416 | (arg:define-identifier ...+ . vararg:define-identifier)} 417 | {~and 418 | {~do (undo!)} 419 | vararg:define-identifier}} 420 | e2:expression ...+ 421 | {~do (pop-scope!)}] ...))) 422 | 423 | (define-syntax-class case-clause 424 | #:datum-literals (quote quasiquote else) 425 | (pattern else) 426 | (pattern ({~and {~or* quote quasiquote} qt} _) 427 | #:when (equal? (syntax-span #'qt) 1) 428 | #:do [(track-error! this-syntax "case clause must be in the form ( ...), not '")]) 429 | (pattern (_ ...))) 430 | 431 | (define-syntax-class cond-expression 432 | #:datum-literals (=> case cond unless when) 433 | (pattern (case 434 | ~! 435 | ce:expression 436 | [:case-clause 437 | (~do (push-scope!)) 438 | e:expression ...+ 439 | (~do (pop-scope!))] ...)) 440 | 441 | (pattern (cond 442 | ~! 443 | [c:expression 444 | (~optional =>) 445 | (~do (push-scope!)) 446 | e:expression ... 447 | (~do (pop-scope!))] ...) 448 | #:do [(for ([clause-stx (in-list (cdr (syntax-e this-syntax)))]) 449 | (unless (eq? (syntax-property clause-stx 'paren-shape) #\[) 450 | (track-warning! clause-stx "this cond clause should be surrounded by square brackets"))) 451 | (unless (eq? (last (syntax->datum #'(c ...))) 'else) 452 | (track-warning! this-syntax "this cond expression does not have an else clause"))]) 453 | 454 | (pattern ({~or unless when} uc:expression 455 | {~do (push-scope!)} 456 | ue:expression ...+ 457 | {~do (pop-scope!)}))) 458 | 459 | (define (try-track-struct-usage! struct-id-stx) 460 | (cond 461 | ;; We don't know for sure that this is a struct 462 | ;; binding, but this is good enough for now. 463 | [(bindings-ref (format-binding "~a" struct-id-stx)) 464 | => (lambda (bi) 465 | (track-binding-usage! (format-binding "~a" struct-id-stx)) 466 | (for ([stx (in-list (binding-info-related-stxes bi))]) 467 | (track-binding-usage! (format-binding "~a" stx))))])) 468 | 469 | (define-syntax-class match-pattern 470 | #:datum-literals (_ ? and app cons else empty or not null quote quasiquote struct* unquote) 471 | (pattern _) 472 | (pattern else 473 | #:do [(track-error! this-syntax "use _ instead of else in the fallthrough case of a match expression")]) 474 | (pattern {~or* null empty} 475 | #:do [(track-error! this-syntax "use '() for match pattern instead of null or empty")]) 476 | (pattern (? e:expression arg:match-pattern ...)) 477 | (pattern ({~or and or} arg:match-pattern ...)) 478 | (pattern (not arg:match-pattern)) 479 | (pattern (app e:expression arg:match-pattern)) 480 | (pattern (struct* struct-id:id ([field-id:id field-pattern:match-pattern] ...)) 481 | #:do [(try-track-struct-usage! #'struct-id)]) 482 | (pattern (cons a:match-pattern b:match-pattern)) 483 | (pattern (quote literal)) 484 | (pattern (quasiquote 485 | {~or (unquote p:match-pattern) 486 | ({~or (unquote e:match-pattern) literal} ...)})) 487 | (pattern (struct-id:id arg:match-pattern ...) 488 | #:do [(try-track-struct-usage! #'struct-id)]) 489 | (pattern id:id #:do [(track-binding! #'id)]) 490 | (pattern e)) 491 | 492 | (define-syntax-class match-expression 493 | #:datum-literals (match match-define match-lambda) 494 | (pattern (match-define ~! 495 | dpat:match-pattern 496 | de:expression)) 497 | 498 | (pattern (match ~! 499 | e:expression 500 | [{~do (push-scope!)} 501 | pat:match-pattern 502 | ce:expression ...+ 503 | {~do (pop-scope!)}] ...+)) 504 | 505 | (pattern (match-lambda 506 | [{~do (push-scope!)} 507 | pat:match-pattern 508 | ce:expression ...+ 509 | {~do (pop-scope!)}] ...+))) 510 | 511 | (define-syntax-class if-expression 512 | #:datum-literals (begin if let) 513 | (pattern (~or (if cond:expression 514 | ((~or begin let) e:expression ...+) 515 | e-else:expression) 516 | (if cond:expression 517 | e-then:expression 518 | ((~or begin let) e:expression ...+))) 519 | #:do [(track-warning! this-syntax "use a cond expression instead of nesting begin or let inside an if")]) 520 | 521 | (pattern (if cond:expression 522 | e-then:expression) 523 | #:do [(track-error! this-syntax "if expressions must contain one expression for the then-branch and another for the else-branch")])) 524 | 525 | (define (define-identifier! 526 | #:check-shadow? [check-shadow? #t] 527 | #:check-usages? [check-usages? #t] 528 | #:related-to [related-to-stx #f] 529 | stx) 530 | (cond 531 | [(name-bound-in-current-scope? (syntax->datum stx)) 532 | (track-error! stx (~a "identifier '" (syntax->datum stx) "' is already defined"))] 533 | 534 | [(and check-shadow? (name-bound? (syntax->datum stx))) 535 | (unless (underscores? (syntax->datum stx)) 536 | (track-warning! stx (~a "identifier '" (syntax->datum stx) "' shadows an earlier binding"))) 537 | 538 | (track-binding! 539 | #:check-usages? check-usages? 540 | #:related-to related-to-stx 541 | stx)] 542 | 543 | [else 544 | (track-binding! 545 | #:check-usages? check-usages? 546 | #:related-to related-to-stx 547 | stx)])) 548 | 549 | (define-syntax-class define-identifier 550 | (pattern id:id 551 | #:do [(define-identifier! #'id)])) 552 | 553 | (define-syntax-class (define-identifier* shadow-ok?) 554 | (pattern id:id 555 | #:do [(define-identifier! 556 | #:check-shadow? (not shadow-ok?) 557 | #'id)])) 558 | 559 | (define-syntax-class define-identifier/new-scope 560 | (pattern id:id 561 | #:do [(push-scope!) 562 | (define-identifier! #'id)])) 563 | 564 | (define-syntax-class define-let-identifier 565 | (pattern [id:id e:expression] 566 | #:do [(unless (eq? (syntax-property this-syntax 'paren-shape) #\[) 567 | (track-warning! this-syntax "bindings within a let should be surrounded by square brackets")) 568 | 569 | (when (name-bound-in-current-scope? (syntax->datum #'id)) 570 | (track-error! #'id (~a "identifier '" (syntax->datum #'id) "' is already defined"))) 571 | 572 | (track-binding! #'id)])) 573 | 574 | (define-syntax-class let-expression 575 | #:datum-literals (let) 576 | (pattern (let 577 | ~! 578 | (~do (push-scope!)) 579 | (~optional proc-id:define-identifier) 580 | (id:define-let-identifier ...) 581 | (~do (push-scope!)) 582 | body:expression ... 583 | (~do (pop-scope!) 584 | (pop-scope!))) 585 | #:do [(when (null? (syntax-e #'(body ...))) 586 | (track-error! this-syntax "let forms must contain at least one body expression"))])) 587 | 588 | (define-syntax-class define-for-clause 589 | (pattern [id:id e:expression] 590 | #:do [(unless (eq? (syntax-property this-syntax 'paren-shape) #\[) 591 | (track-error! this-syntax "'for' clauses should be surrounded by square brackets")) 592 | (when (name-bound-in-current-scope? (syntax->datum #'id)) 593 | (track-error! #'id (~a "identifier '" (syntax->datum #'id) "' is already defined in another clause"))) 594 | (track-binding! #'id)]) 595 | (pattern [(ids:id ...) e:expression] 596 | #:do [(unless (eq? (syntax-property this-syntax 'paren-shape) #\[) 597 | (track-error! this-syntax "'for' clauses should be surrounded by square brackets")) 598 | (for ([id-stx (in-list (syntax-e #'(ids ...)))]) 599 | (when (name-bound-in-current-scope? (syntax->datum id-stx)) 600 | (track-error! #'id (~a "identifier '" (syntax->datum id-stx) "' is already defined in another clause"))) 601 | (track-binding! id-stx))])) 602 | 603 | (define-splicing-syntax-class for-keyword 604 | (pattern (~seq #:do [do-e:expression ...])) 605 | (pattern (~seq (~or #:when #:unless #:break #:final) e:expression))) 606 | 607 | (define-syntax-class for-expression 608 | #:datum-literals (for for* 609 | for/and for*/and 610 | for/or for*/or 611 | for/first for*/first 612 | for/list for*/list 613 | for/hash for*/hash 614 | for/hasheq for*/hasheq 615 | for/hasheqv for*/hasheqv 616 | for/vector for*/vector 617 | for/fold for*/fold 618 | for/lists for*/lists) 619 | (pattern ((~or for for* 620 | for/and for*/and 621 | for/or for*/or 622 | for/first for*/first 623 | for/list for*/list 624 | for/hash for*/hash 625 | for/hasheq for*/hasheq 626 | for/hasheqv for*/hasheqv) 627 | ~! 628 | (~do (push-scope!)) 629 | ((~or clause:define-for-clause kwd-clause:for-keyword) ...) 630 | (~do (push-scope!)) 631 | (~or (~seq (~or #:break #:final) break-e:expression) body:expression) ... 632 | (~do (pop-scope!) 633 | (pop-scope!))) 634 | #:do [(when (null? (syntax-e #'(body ...))) 635 | (track-error! this-syntax "for forms must contain at least one body expression"))]) 636 | 637 | (pattern ((~or for/vector for*/vector) 638 | ~! 639 | (~optional (~seq #:length length-e:expression)) 640 | (~do (push-scope!)) 641 | ((~or clause:define-for-clause kwd-clause:for-keyword) ...) 642 | (~do (push-scope!)) 643 | (~or (~seq (~or #:break #:final) break-e:expression) body:expression) ... 644 | (~do (pop-scope!) 645 | (pop-scope!))) 646 | #:do [(when (null? (syntax-e #'(body ...))) 647 | (track-error! this-syntax "for forms must contain at least one body expression"))]) 648 | 649 | (pattern ((~or for/fold for*/fold) 650 | ~! 651 | (~do (push-scope!) 652 | (push-scope!)) 653 | (accum-id:define-for-clause ... (~optional (~seq #:result result-e:expression))) 654 | (~do (stash-scope!)) 655 | ((~or clause:define-for-clause kwd-clause:for-keyword) ...) 656 | (~do (restore-scope!)) 657 | (~or (~seq (~or #:break #:final) break-e:expression) body:expression) ... 658 | (~do (pop-scope!) 659 | (pop-scope!))) 660 | #:do [(when (null? (syntax-e #'(body ...))) 661 | (track-error! this-syntax "for forms must contain at least one body expression"))]) 662 | 663 | (pattern ((~or for/lists for*/lists) 664 | ~! 665 | (~do (push-scope!)) 666 | (id:define-identifier ... (~optional (~seq #:result result-e:expression))) 667 | (~do (push-scope!)) 668 | ((~or clause:define-for-clause kwd-clause:for-keyword) ...) 669 | (~do (push-scope!)) 670 | (~or (~seq (~or #:break #:final) break-e:expression) body:expression) ... 671 | (~do (pop-scope!) 672 | (pop-scope!) 673 | (pop-scope!))) 674 | #:do [(when (null? (syntax-e #'(body ...))) 675 | (track-error! this-syntax "for forms must contain at least one body expression"))])) 676 | 677 | ;; Expressions that introduce new scopes, like rackunit's `test-case'. 678 | (define-syntax-class scoping-expression-id 679 | #:datum-literals (delay delay/sync delay/thread test-case test-suite) 680 | (pattern {~or delay delay/sync delay/thread test-case test-suite})) 681 | 682 | (define-syntax-class scoping-expression 683 | (pattern (id:scoping-expression-id ~! 684 | (~do (push-scope!)) 685 | e:expression ... 686 | (~do (pop-scope!))))) 687 | 688 | (define-syntax-class function-argument 689 | #:commit 690 | (pattern arg:define-identifier) 691 | (pattern [arg:id default:expression] 692 | ;; This pattern must call define identifier _after_ the 693 | ;; default expression is linted to avoid adding an identifier 694 | ;; to the current scope that could potentially shadow an 695 | ;; identifier used by the default expression. See the 696 | ;; "shadow-arg.rkt" test. 697 | #:do [(define-identifier! #'arg)])) 698 | 699 | ;; A user of this pattern must apply (attribute p.pop-scopes!) to pop 700 | ;; all the scopes it may have introduced while walking the nested 701 | ;; function headers. 702 | (define-syntax-class (function-header shadow-ok?) 703 | #:commit 704 | (pattern {~var fun (define-identifier* shadow-ok?)} 705 | #:attr name #''fun 706 | #:attr depth 0 707 | #:attr pop-scopes! void) 708 | 709 | (pattern {~and 710 | {~or 711 | {~and 712 | {~do (save!)} 713 | ({~var fun (function-header shadow-ok?)} 714 | {~do (push-scope!)} ;; must be popped by the user according to depth 715 | {~seq {~optional k:keyword} arg:function-argument} ...)} 716 | {~and 717 | {~do (undo!)} 718 | ({~var fun (function-header shadow-ok?)} 719 | {~do (push-scope!)} ;; must be popped by the user according to depth 720 | {~seq {~optional k:keyword} arg:function-argument} ... 721 | . vararg:define-identifier)}}} 722 | #:attr name (attribute fun.name) 723 | #:attr depth (add1 (attribute fun.depth)) 724 | #:attr pop-scopes! (lambda () 725 | (for ([_ (in-range (attribute depth))]) 726 | (pop-scope!))))) 727 | 728 | (define-syntax-class class-define 729 | (pattern {~or {~datum define/public} 730 | {~datum define/pubment} 731 | {~datum define/public-final} 732 | {~datum define/override} 733 | {~datum define/overment} 734 | {~datum define/override-final} 735 | {~datum define/augment} 736 | {~datum define/augride} 737 | {~datum define/augment-final} 738 | {~datum define/private}})) 739 | 740 | (define-syntax-class define-like 741 | (pattern id:id #:when (string-prefix? (symbol->string (syntax->datum #'id)) "define"))) 742 | 743 | (define-syntax-class definition 744 | #:datum-literals (define-generics define-logger define-syntax define-syntax-class define-syntax-rule define-syntax-parser define-system define-values define/match) 745 | #:commit 746 | (pattern (define-syntax name:id ~! stx-e) 747 | #:do [(track-binding! #'name)]) 748 | 749 | (pattern (define-syntax ~! (name:id stx-arg) 750 | stx-e ...+) 751 | #:do [(track-binding! #'name)]) 752 | 753 | (pattern (define-syntax-class ~! name:id . _rest) 754 | #:do [(track-binding! #'name #:check-usages? #f)]) 755 | 756 | (pattern (define-syntax-rule ~! (name:id arg ...) 757 | stx-e ...+) 758 | #:do [(track-binding! #'name)]) 759 | 760 | (pattern (define-syntax-parser ~! name:id stx-e ...+) 761 | #:do [(track-binding! #'name)]) 762 | 763 | (pattern (define-values (name:id ...+) 764 | ~! 765 | (~do (push-scope!)) 766 | e:expression ...+ 767 | (~do (pop-scope!))) 768 | #:do [(for ([name (in-list (syntax->list #'(name ...)))]) 769 | (define-identifier! name))]) 770 | 771 | ;; from racket/generic 772 | (pattern (define-generics ~! name:id 773 | (fn-name:id arg:id ...) ... 774 | e ...) 775 | #:do [(define gen-id (format-id #'name "gen:~a" #'name #:source #'name)) 776 | (define-identifier! gen-id) 777 | (define-identifier! 778 | #:related-to gen-id 779 | (format-id #'name "~a?" #'name #:source #'name) ) 780 | (for ([fn (in-list (syntax->list #'((fn-name arg ...) ...)))]) 781 | (define parts (syntax->list fn)) 782 | (define fn-name (car parts)) 783 | (define fn-args (cdr parts)) 784 | (define-identifier! #:related-to gen-id fn-name) 785 | 786 | (define generic-found? 787 | (for/first ([fn-arg (in-list fn-args)] 788 | #:when (eq? (syntax->datum fn-arg) 789 | (syntax->datum #'name))) #t)) 790 | 791 | (unless generic-found? 792 | (track-warning! fn-name (~a "generic identifier '" (syntax->datum #'name) "' not bound in definition"))))]) 793 | 794 | (pattern (define-logger ~! name:id e ...) 795 | #:do [(define-identifier! #:check-usages? #f (format-id #'name "~a-logger" #'name #:source #'name)) 796 | (define-identifier! #:check-usages? #f (format-id #'name "log-~a-fatal" #'name #:source #'name)) 797 | (define-identifier! #:check-usages? #f (format-id #'name "log-~a-error" #'name #:source #'name)) 798 | (define-identifier! #:check-usages? #f (format-id #'name "log-~a-warning" #'name #:source #'name)) 799 | (define-identifier! #:check-usages? #f (format-id #'name "log-~a-info" #'name #:source #'name)) 800 | (define-identifier! #:check-usages? #f (format-id #'name "log-~a-debug" #'name #:source #'name))]) 801 | 802 | ;; from component-lib 803 | (pattern (define-system ~! name:id e ...) 804 | #:do [(track-binding! #'name "~a-system")]) 805 | 806 | (pattern (define/match 807 | {~var hdr (function-header #f)} 808 | ~! 809 | {~do (push-scope!)} 810 | c:match-pattern ...+) 811 | #:do [(pop-scope!) 812 | ((attribute hdr.pop-scopes!))]) 813 | 814 | (pattern (_:class-define name:id ~! e:expression)) 815 | (pattern (_:class-define 816 | (name:id 817 | ~! 818 | {~do (push-scope!)} 819 | {~seq {~optional kwd:keyword} arg:function-argument} ...) 820 | {~do (push-scope!)} 821 | e:expression ...+) 822 | #:do [(pop-scope!) 823 | (pop-scope!)]) 824 | 825 | (pattern (define-id:define-like 826 | name:define-identifier 827 | ~! 828 | e:expression ...+) 829 | #:do [(track-binding-usage! (format-binding "~a" #'define-id))]) 830 | 831 | (pattern (define-id:define-like 832 | {~var hdr (function-header #f)} 833 | ~! 834 | {~do (push-scope!)} 835 | e:expression ...+) 836 | #:do [(track-binding-usage! (format-binding "~a" #'define-id)) 837 | (pop-scope!) 838 | ((attribute hdr.pop-scopes!))])) 839 | 840 | (define (check-requires-sorted stxs mod-stxs type-stxs) 841 | (for ([m1 (in-list (syntax->datum mod-stxs))] 842 | [t1 (in-list (syntax->datum type-stxs))] 843 | [s1 (in-list (syntax-e stxs))] 844 | [m2 (in-list (cdr (syntax->datum mod-stxs)))] 845 | [t2 (in-list (cdr (syntax->datum type-stxs)))] 846 | [s2 (in-list (cdr (syntax-e stxs)))]) 847 | (cond 848 | [(and (eq? t2 'syntax) 849 | (not (eq? t1 t2))) 850 | (track-warning! s2 (format "require (for-syntax ...) should come before all others"))] 851 | 852 | [(and (eq? t1 'relative) 853 | (eq? t2 'absolute)) 854 | (track-warning! s1 (format "require ~.s should come after ~.s" m1 m2))] 855 | 856 | [(and (eq? t1 t2) 857 | (stringstring (syntax-e #'mod)) 864 | #:do [(define mod-sym (syntax->datum #'mod)) 865 | (case mod-sym 866 | [(racket/contract) 867 | (track-warning! #'mod "prefer racket/contract/base if possible")] 868 | [(syntax/parse) 869 | (track-warning! #'mod "prefer syntax/parse/pre if possible")])]) 870 | (pattern mod:string 871 | #:with t 'relative 872 | #:with s #'mod)) 873 | 874 | (define-syntax-class require-spec 875 | #:datum-literals (combine-in except-in for-syntax only-in prefix-in submod) 876 | (pattern mod:root-module-path 877 | #:with t #'mod.t 878 | #:with s #'mod.s) 879 | (pattern (for-syntax e:require-spec ...) 880 | #:with t 'syntax 881 | #:with s "") 882 | (pattern (only-in spec:require-spec id ...) 883 | #:with t #'spec.t 884 | #:with s #'spec.s) 885 | (pattern (except-in spec:require-spec id ...) 886 | #:with t #'spec.t 887 | #:with s #'spec.s) 888 | (pattern (prefix-in prefix:id spec:require-spec) 889 | #:with t #'spec.t 890 | #:with s #'spec.s) 891 | (pattern (combine-in) 892 | #:with t 'absolute 893 | #:with s "" 894 | #:do [(track-warning! this-syntax "empty combine-in")]) 895 | (pattern (combine-in spec0:require-spec spec:require-spec ...) 896 | #:with t #'spec0.t 897 | #:with s #'spec0.s 898 | #:do [(check-requires-sorted 899 | #'(spec0 spec ...) 900 | #'(spec0.s spec.s ...) 901 | #'(spec0.t spec.t ...))]) 902 | (pattern (submod spec0:require-spec spec:require-spec ...) 903 | #:with t #'spec0.t 904 | #:with s #'spec0.s)) 905 | 906 | (define-syntax-class require-statement 907 | #:datum-literals (require) 908 | (pattern (require e:require-spec ...+) 909 | #:do [(check-requires-sorted 910 | #'(e ...) 911 | #'(e.s ...) 912 | #'(e.t ...))])) 913 | 914 | (define-syntax-class provide-renamed-id 915 | (pattern id:id) 916 | (pattern e 917 | #:do [(track-error! #'e "not an identifier")])) 918 | 919 | (define-syntax-class provide-spec 920 | #:datum-literals (all-defined-out contract-out rename rename-out struct struct-out) 921 | (pattern id:id 922 | #:do [(track-provided! #'id)]) 923 | 924 | (pattern (all-defined-out) 925 | #:do [(track-all-provided!)]) 926 | 927 | (pattern (contract-out 928 | ~! 929 | {~do (push-scope!)} ;; push a scope here so bindings are punted on 930 | {~or* [{~optional struct} name:id e:expression] 931 | [rename name:id provided-name:id e:expression]} ...) 932 | #:do [(for-each track-provided! (syntax-e #'(name ...))) 933 | (for-each track-related-provided! (syntax-e #'(name ...))) 934 | (pop-scope!)]) 935 | 936 | (pattern (rename-out ~! [to-rename-id:id renamed-id:provide-renamed-id] ...) 937 | #:do [(for-each track-provided! (syntax-e #'(to-rename-id ...)))]) 938 | 939 | (pattern (struct-out ~! struct-id:id) 940 | #:do [(track-provided! #'struct-id) 941 | (track-related-provided! #'struct-id) 942 | (cond 943 | [(bindings-ref (format-binding "~a" #'struct-id)) 944 | => (lambda (bi) 945 | (for ([stx (in-list (binding-info-related-stxes bi))]) 946 | (track-provided! stx)))])]) 947 | 948 | (pattern e)) 949 | 950 | (define-syntax-class provide-statement 951 | #:datum-literals (provide) 952 | (pattern (provide e:provide-spec ...+))) 953 | 954 | (define-syntax-class define-struct-identifier 955 | (pattern name:id 956 | #:do [(track-binding! #'name) 957 | (track-binding! #'name "~a?" 958 | #:check-usages? #f)])) 959 | 960 | (define-syntax-class struct-field-spec 961 | (pattern name:id 962 | #:with mutable? #f) 963 | (pattern [name:id (~alt (~and #:mutable field-mutable) e) ...] 964 | #:with mutable? #'(~? (field-mutable ...) #f))) 965 | 966 | (define-syntax-class struct++-field-spec 967 | (pattern (name:id)) 968 | (pattern (name:id c ...+)) 969 | (pattern ([name:id e:expression] c ...+))) 970 | 971 | (define-syntax-class struct-method-definition 972 | #:datum-literals (define define/generic) 973 | (pattern (define/generic local-id:id method-id:id) 974 | #:do [(track-binding! #'local-id)]) 975 | (pattern (define id:id e:expression) 976 | #:do [(track-binding! #'id #:check-usages? #f)]) 977 | (pattern (define {~var hdr (function-header #t)} body-e:expression ...+) 978 | #:do [(define method-name (syntax-e (cadr (syntax-e #'hdr.name)))) 979 | (track-binding-usage! method-name) 980 | ((attribute hdr.pop-scopes!))])) 981 | 982 | (define-syntax-class struct-definition 983 | #:datum-literals (serializable-struct serializable-struct/versions struct struct/contract struct++) 984 | (pattern ((~or serializable-struct 985 | serializable-struct/versions 986 | struct 987 | struct/contract) 988 | ~! 989 | name:id 990 | (~optional super-id:identifier-expression) 991 | (~optional version:number) 992 | (field:struct-field-spec ...) 993 | (~alt {~optional (~seq #:name name-override:id)} 994 | (~and #:mutable struct-mutable) 995 | (~seq #:property P:expression PV:expression) 996 | (~seq #:methods ~! G:identifier-expression [md ...]) 997 | e) ...) 998 | #:do [(define name-stx #'{~? name-override name}) 999 | (track-binding! name-stx) 1000 | (track-binding! #'name "~a?" #:check-usages? #f #:related-to name-stx) 1001 | (define prefix (symbol->string (format-binding "~a" #'name))) 1002 | (define mutable? (not (null? (syntax->datum #'(struct-mutable ...))))) 1003 | (for-each (lambda (stx field-mutable?) 1004 | (track-binding! stx (string-append prefix "-~a") #:related-to name-stx) 1005 | (when (or mutable? field-mutable?) 1006 | (track-binding! stx (string-append "set-" prefix "-~a!") #:related-to name-stx))) 1007 | (syntax-e #'(field.name ...)) 1008 | (map (compose1 not not syntax-e) (syntax-e #'(field.mutable? ...)))) 1009 | ;; Using try-track-struct-usage! here ensures that G 1010 | ;; and all of its related definitions are tracked as 1011 | ;; used. 1012 | (for ([G-stx (in-list (syntax-e #'(G ...)))]) 1013 | (define G-sym (syntax-e G-stx)) 1014 | (try-track-struct-usage! G-sym)) 1015 | (push-scope!) 1016 | (syntax-parse #'(md ... ...) 1017 | [(d:struct-method-definition ...) (void)]) 1018 | (pop-scope!)]) 1019 | 1020 | (pattern (struct++ 1021 | ~! 1022 | name:id 1023 | (~optional super-id:identifier-expression) 1024 | (spec:struct++-field-spec ...) 1025 | e ...) 1026 | #:do [(track-binding! #'name #:check-usages? #f) 1027 | (track-binding! #'name "~a?" #:check-usages? #f) 1028 | (track-binding! #'name "~a++") 1029 | (for-each (lambda (stx) 1030 | (track-binding! stx (string-append (symbol->string (format-binding "~a" #'name)) "-~a") #:check-usages? #f) 1031 | (track-binding! stx (string-append (symbol->string (format-binding "set-~a" #'name)) "-~a") #:check-usages? #f) 1032 | (track-binding! stx (string-append (symbol->string (format-binding "update-~a" #'name)) "-~a") #:check-usages? #f)) 1033 | (syntax-e #'(spec.name ...)))])) 1034 | 1035 | (define extensions 1036 | (for*/list ([path (find-relevant-directories '(review-exts))] 1037 | [bind (in-list ((get-info/full path) 'review-exts))]) 1038 | (match-define (list mod predicate-id extension-id) bind) 1039 | (cons 1040 | (dynamic-require mod predicate-id) 1041 | (dynamic-require mod extension-id)))) 1042 | 1043 | (define (find-extension stx) 1044 | (findf (λ (ext) ((car ext) stx)) extensions)) 1045 | 1046 | (define-syntax-class extension 1047 | (pattern e 1048 | #:when (find-extension this-syntax) 1049 | #:do [(with-handlers ([exn:fail? 1050 | (lambda (e) 1051 | ((error-display-handler) 1052 | (exn-message e) 1053 | e))]) 1054 | (parameterize ([current-reviewer 1055 | (reviewer 1056 | (λ (stx) 1057 | (syntax-parse stx 1058 | [e:expression #'e])) 1059 | track-error! 1060 | track-warning! 1061 | track-binding! 1062 | try-track-struct-usage! 1063 | push-scope! 1064 | pop-scope!)]) 1065 | ((cdr (find-extension this-syntax)) this-syntax)))])) 1066 | 1067 | (define-syntax-class expression 1068 | #:commit 1069 | (pattern ext:extension) 1070 | (pattern d:definition) 1071 | (pattern s:struct-definition) 1072 | (pattern l:lambda-expression) 1073 | (pattern c:match-expression) 1074 | (pattern c:cond-expression) 1075 | (pattern i:if-expression) 1076 | (pattern l:let-expression) 1077 | (pattern f:for-expression) 1078 | (pattern s:scoping-expression) 1079 | (pattern c:contract-arrow-expression) 1080 | (pattern a:application-expression) 1081 | (pattern I:identifier-expression) 1082 | (pattern S:string) 1083 | (pattern N:number) 1084 | (pattern B:boolean) 1085 | (pattern K:keyword) 1086 | (pattern e)) 1087 | 1088 | (define-syntax-class toplevel 1089 | #:commit 1090 | (pattern M:module) 1091 | (pattern m:submodule) 1092 | (pattern r:require-statement) 1093 | (pattern p:provide-statement) 1094 | (pattern e:expression)) 1095 | 1096 | (define-syntax-class module 1097 | #:datum-literals (module #%module-begin) 1098 | (pattern (module _name:id _path:id 1099 | (#%module-begin ~! _e:toplevel ...)) 1100 | #:do [(check-unused-bindings!)])) 1101 | 1102 | (define-syntax-class submodule 1103 | #:datum-literals (module module+) 1104 | (pattern (module ~! _name _path _e ...) 1105 | #:do [(lint-submodule-syntax!/trampoline this-syntax)]) 1106 | 1107 | (pattern (module+ ~! 1108 | _name:id 1109 | (~do (push-scope!)) 1110 | _e:toplevel ... 1111 | (~do (pop-scope!))))) 1112 | -------------------------------------------------------------------------------- /media/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/media/screenshot.png -------------------------------------------------------------------------------- /problem.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | (struct-out problem) 5 | problemstring linter-tests) "")) 29 | 30 | (for ([filename (directory-list linter-tests)] 31 | #:when (bytes=? (path-get-extension filename) #".rkt")) 32 | (define-values (in out) (make-pipe)) 33 | (define input-filepath (build-path linter-tests filename)) 34 | (define output-filepath (~a input-filepath ".out")) 35 | 36 | (match-define (list _ _ _ _ control) 37 | (process*/ports out #f out (find-executable-path "raco") "review" input-filepath)) 38 | (control 'wait) 39 | 40 | (close-output-port out) 41 | (define command-output (map strip-prefixes (port->lines in))) 42 | (define expected-output 43 | (cond 44 | [(file-exists? output-filepath) 45 | (call-with-input-file output-filepath port->lines)] 46 | 47 | [else 48 | (begin0 command-output 49 | (update-output-file! output-filepath command-output))])) 50 | 51 | (unless (equal? command-output expected-output) 52 | (displayln (~a "output differs when linting " output-filepath)) 53 | (displayln "expected:") 54 | (for-each (compose1 displayln indent) expected-output) 55 | (displayln "found:") 56 | (for-each (compose1 displayln indent) command-output) 57 | (display "update? ") 58 | (let loop () 59 | (if (getenv "BATCH") 60 | (exit 1) 61 | (case (read-char) 62 | [(#\y) (update-output-file! output-filepath command-output)] 63 | [(#\n) (exit 1)] 64 | [else (loop)]))))) 65 | -------------------------------------------------------------------------------- /tests/lint/already-defined-argument.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define (f g g) 4 | g) 5 | -------------------------------------------------------------------------------- /tests/lint/already-defined-argument.rkt.out: -------------------------------------------------------------------------------- 1 | /already-defined-argument.rkt:3:10:warning:identifier 'f' is never used 2 | /already-defined-argument.rkt:3:14:error:identifier 'g' is already defined 3 | -------------------------------------------------------------------------------- /tests/lint/already-defined-let.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (let ([x 5] 4 | [x 42]) 5 | x) 6 | 7 | (let loop ([x 5] 8 | [x 42]) 9 | x) 10 | 11 | (define (f x) 12 | (let ([x 5] 13 | [x 10]) 14 | x)) 15 | -------------------------------------------------------------------------------- /tests/lint/already-defined-let.rkt.out: -------------------------------------------------------------------------------- 1 | /already-defined-let.rkt:4:8:error:identifier 'x' is already defined 2 | /already-defined-let.rkt:7:6:warning:identifier 'loop' is never used 3 | /already-defined-let.rkt:8:13:error:identifier 'x' is already defined 4 | /already-defined-let.rkt:11:10:warning:identifier 'f' is never used 5 | /already-defined-let.rkt:11:12:warning:identifier 'x' is never used 6 | /already-defined-let.rkt:13:10:error:identifier 'x' is already defined 7 | -------------------------------------------------------------------------------- /tests/lint/already-defined-values.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define-values (a a b) 4 | (values 1 2 3)) 5 | 6 | (define-values (a _ _) 7 | (values 1 2 3)) 8 | 9 | (define-values (a _ _) 10 | (define-values (b b) 11 | (values 1 2)) 12 | (values b b)) 13 | 14 | (define-values (x y z) 15 | (values 1 2 3)) 16 | -------------------------------------------------------------------------------- /tests/lint/already-defined-values.rkt.out: -------------------------------------------------------------------------------- 1 | /already-defined-values.rkt:3:17:warning:identifier 'a' is never used 2 | /already-defined-values.rkt:3:19:error:identifier 'a' is already defined 3 | /already-defined-values.rkt:3:21:warning:identifier 'b' is never used 4 | /already-defined-values.rkt:6:17:error:identifier 'a' is already defined 5 | /already-defined-values.rkt:6:21:error:identifier '_' is already defined 6 | /already-defined-values.rkt:9:17:error:identifier 'a' is already defined 7 | /already-defined-values.rkt:9:19:error:identifier '_' is already defined 8 | /already-defined-values.rkt:9:21:error:identifier '_' is already defined 9 | /already-defined-values.rkt:10:19:warning:identifier 'b' shadows an earlier binding 10 | /already-defined-values.rkt:10:21:error:identifier 'b' is already defined 11 | /already-defined-values.rkt:14:17:warning:identifier 'x' is never used 12 | /already-defined-values.rkt:14:19:warning:identifier 'y' is never used 13 | /already-defined-values.rkt:14:21:warning:identifier 'z' is never used 14 | -------------------------------------------------------------------------------- /tests/lint/already-defined.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define a 42) 4 | (define a 43) 5 | (define (a x) x) 6 | (define ((a x) y) (+ x y)) 7 | (define (((a x) y) z) (+ x y z)) 8 | -------------------------------------------------------------------------------- /tests/lint/already-defined.rkt.out: -------------------------------------------------------------------------------- 1 | /already-defined.rkt:3:9:warning:identifier 'a' is never used 2 | /already-defined.rkt:4:9:error:identifier 'a' is already defined 3 | /already-defined.rkt:5:10:error:identifier 'a' is already defined 4 | /already-defined.rkt:6:11:error:identifier 'a' is already defined 5 | /already-defined.rkt:7:12:error:identifier 'a' is already defined 6 | -------------------------------------------------------------------------------- /tests/lint/bad-brackets.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (let ((x 1) 4 | [y 2] 5 | (z 3)) 6 | x) 7 | -------------------------------------------------------------------------------- /tests/lint/bad-brackets.rkt.out: -------------------------------------------------------------------------------- 1 | /bad-brackets.rkt:3:7:warning:bindings within a let should be surrounded by square brackets 2 | /bad-brackets.rkt:4:8:warning:identifier 'y' is never used 3 | /bad-brackets.rkt:5:7:warning:bindings within a let should be surrounded by square brackets 4 | /bad-brackets.rkt:5:8:warning:identifier 'z' is never used 5 | -------------------------------------------------------------------------------- /tests/lint/begin-inside-if.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (if #t 4 | #f 5 | (let () 6 | 1)) 7 | 8 | (if #t 9 | (begin #f) 10 | #t) 11 | -------------------------------------------------------------------------------- /tests/lint/begin-inside-if.rkt.out: -------------------------------------------------------------------------------- 1 | /begin-inside-if.rkt:3:1:warning:use a cond expression instead of nesting begin or let inside an if 2 | /begin-inside-if.rkt:8:1:warning:use a cond expression instead of nesting begin or let inside an if 3 | -------------------------------------------------------------------------------- /tests/lint/binding-else-in-match.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/match) 4 | 5 | (match 10 6 | [x x] 7 | [else 'foo]) 8 | -------------------------------------------------------------------------------- /tests/lint/binding-else-in-match.rkt.out: -------------------------------------------------------------------------------- 1 | /binding-else-in-match.rkt:7:4:error:use _ instead of else in the fallthrough case of a match expression 2 | -------------------------------------------------------------------------------- /tests/lint/case-lamba.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define g 4 | (case-lambda 5 | [(x) (g x 1)] 6 | [(x y) (f (+ x y))])) 7 | 8 | (define (f a) 9 | a) 10 | -------------------------------------------------------------------------------- /tests/lint/case-lamba.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/case-lamba.rkt.out -------------------------------------------------------------------------------- /tests/lint/case.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define a 1) 4 | 5 | (case 42 6 | [(1 2 3) 7 | (define a 1) 8 | (define b 2) 9 | (+ a b)] 10 | 11 | [(42) 12 | (define b 1) 13 | b]) 14 | 15 | (case 'b 16 | ['b 1]) 17 | -------------------------------------------------------------------------------- /tests/lint/case.rkt.out: -------------------------------------------------------------------------------- 1 | /case.rkt:3:9:warning:identifier 'a' is never used 2 | /case.rkt:7:12:warning:identifier 'a' shadows an earlier binding 3 | /case.rkt:16:4:error:case clause must be in the form ( ...), not ' 4 | -------------------------------------------------------------------------------- /tests/lint/component-lib-system.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require component) 4 | 5 | (provide dev-system) 6 | 7 | (define-system dev) 8 | 9 | (define-system prod) 10 | -------------------------------------------------------------------------------- /tests/lint/component-lib-system.rkt.out: -------------------------------------------------------------------------------- 1 | /component-lib-system.rkt:9:16:warning:identifier 'prod-system' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/cond-bad-brackets.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (cond 4 | (void) 5 | [(void)] 6 | (#t 'a) 7 | [#f 'b] 8 | (0 'c) 9 | {1 'd} 10 | [else 'e]) 11 | -------------------------------------------------------------------------------- /tests/lint/cond-bad-brackets.rkt.out: -------------------------------------------------------------------------------- 1 | /cond-bad-brackets.rkt:4:3:warning:this cond clause should be surrounded by square brackets 2 | /cond-bad-brackets.rkt:6:3:warning:this cond clause should be surrounded by square brackets 3 | /cond-bad-brackets.rkt:8:3:warning:this cond clause should be surrounded by square brackets 4 | /cond-bad-brackets.rkt:9:3:warning:this cond clause should be surrounded by square brackets 5 | -------------------------------------------------------------------------------- /tests/lint/cond-without-else.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define x 0) 4 | 5 | (cond 6 | [(> x 0) 'positive]) 7 | 8 | (define res 9 | (cond 10 | [(> x 0) 'positive])) 11 | 12 | (define (f x) 13 | (cond 14 | [(> x 0) 'positive])) 15 | 16 | (cond 17 | [(void)]) 18 | 19 | (cond 20 | [#f 1] 21 | [else 2]) 22 | -------------------------------------------------------------------------------- /tests/lint/cond-without-else.rkt.out: -------------------------------------------------------------------------------- 1 | /cond-without-else.rkt:5:1:warning:this cond expression does not have an else clause 2 | /cond-without-else.rkt:8:9:warning:identifier 'res' is never used 3 | /cond-without-else.rkt:9:3:warning:this cond expression does not have an else clause 4 | /cond-without-else.rkt:12:10:warning:identifier 'f' is never used 5 | /cond-without-else.rkt:12:12:warning:identifier 'x' shadows an earlier binding 6 | /cond-without-else.rkt:13:3:warning:this cond expression does not have an else clause 7 | /cond-without-else.rkt:16:1:warning:this cond expression does not have an else clause 8 | -------------------------------------------------------------------------------- /tests/lint/contracts.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/contract/base 4 | racket/contract/region) 5 | 6 | (define/contract (a x y [s 'a]) 7 | (-> [natural-number/c natural-number/c] [(or/c 'a 'b)] void?) 8 | (void)) 9 | 10 | (define/contract (b a [b #f]) 11 | (->* (natural-number/c) 12 | ((or/c #f number?)) 13 | any) 14 | (void)) 15 | 16 | false/c 17 | -------------------------------------------------------------------------------- /tests/lint/contracts.rkt.out: -------------------------------------------------------------------------------- 1 | /contracts.rkt:6:21:warning:identifier 'x' is never used 2 | /contracts.rkt:6:23:warning:identifier 'y' is never used 3 | /contracts.rkt:6:26:warning:identifier 's' is never used 4 | /contracts.rkt:7:7:warning:contract domain expressions should use round parens; did you mean to use ->*? 5 | /contracts.rkt:7:43:warning:contract domain expressions should use round parens; did you mean to use ->*? 6 | /contracts.rkt:10:21:warning:identifier 'a' is never used 7 | /contracts.rkt:10:21:warning:identifier 'a' shadows an earlier binding 8 | /contracts.rkt:10:24:warning:identifier 'b' is never used 9 | /contracts.rkt:10:24:warning:identifier 'b' shadows an earlier binding 10 | /contracts.rkt:11:8:warning:mandatory domain parens should be square 11 | /contracts.rkt:12:8:warning:optional domain parens should be square 12 | /contracts.rkt:16:1:warning:prefer #f over false/c 13 | -------------------------------------------------------------------------------- /tests/lint/define-generics.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/generic) 4 | 5 | (provide 6 | gen:to-jsexpr 7 | ->jsexpr) 8 | 9 | (define-generics to-jsexpr 10 | (->jsexpr to-jsexpr) 11 | (->jsexpr/options wat to-jsexpr) 12 | (->jsexpr/bad) 13 | #:fast-defaults 14 | ()) 15 | -------------------------------------------------------------------------------- /tests/lint/define-generics.rkt.out: -------------------------------------------------------------------------------- 1 | /define-generics.rkt:9:18:warning:identifier 'to-jsexpr?' is never used 2 | /define-generics.rkt:11:4:warning:identifier '->jsexpr/options' is never used 3 | /define-generics.rkt:12:4:warning:identifier '->jsexpr/bad' is never used 4 | /define-generics.rkt:12:4:warning:generic identifier 'to-jsexpr' not bound in definition 5 | -------------------------------------------------------------------------------- /tests/lint/define-logger.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | log-example-debug 5 | do-something-else) 6 | 7 | (define log-example-error 1) 8 | 9 | (define-logger example) 10 | 11 | (log-example-info "hello!") 12 | -------------------------------------------------------------------------------- /tests/lint/define-logger.rkt.out: -------------------------------------------------------------------------------- 1 | /define-logger.rkt:5:2:warning:identifier 'do-something-else' provided but not defined 2 | /define-logger.rkt:7:9:warning:identifier 'log-example-error' is never used 3 | /define-logger.rkt:9:16:error:identifier 'log-example-error' is already defined 4 | -------------------------------------------------------------------------------- /tests/lint/define-match.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/match) 4 | 5 | (define/match (f x) 6 | [(1) 7 | (define x+1 (add1 x)) 8 | x+1] 9 | [(2) 10 | (define x+1 (add1 x)) 11 | x+1]) 12 | 13 | (f 1) 14 | -------------------------------------------------------------------------------- /tests/lint/define-match.rkt.out: -------------------------------------------------------------------------------- 1 | /define-match.rkt:5:18:warning:identifier 'x' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/define-syntax-rule.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require (for-syntax racket/base)) 4 | 5 | (define-syntax-rule (define-keywords id ...) 6 | (begin 7 | (provide id ...) 8 | (define-syntax (id stx) 9 | (raise-syntax-error 'id "not allowed outside special form" stx)) ...)) 10 | 11 | (define-keywords foo bar baz) 12 | -------------------------------------------------------------------------------- /tests/lint/define-syntax-rule.rkt.out: -------------------------------------------------------------------------------- 1 | /define-syntax-rule.rkt:11:18:warning:identifier 'foo' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/define-values-shadowing.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define-values (a b) 4 | (let () 5 | (define a 42) 6 | (define b 20) 7 | (+ a b))) 8 | 9 | (define (a x) 10 | x) 11 | 12 | b 13 | -------------------------------------------------------------------------------- /tests/lint/define-values-shadowing.rkt.out: -------------------------------------------------------------------------------- 1 | /define-values-shadowing.rkt:3:17:warning:identifier 'a' is never used 2 | /define-values-shadowing.rkt:9:10:error:identifier 'a' is already defined 3 | -------------------------------------------------------------------------------- /tests/lint/empty-let.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (let ([x 1])) 4 | 5 | (let loop ()) 6 | 7 | (let loop () 8 | (loop)) 9 | -------------------------------------------------------------------------------- /tests/lint/empty-let.rkt.out: -------------------------------------------------------------------------------- 1 | /empty-let.rkt:3:1:error:let forms must contain at least one body expression 2 | /empty-let.rkt:3:8:warning:identifier 'x' is never used 3 | /empty-let.rkt:5:1:error:let forms must contain at least one body expression 4 | /empty-let.rkt:5:6:warning:identifier 'loop' is never used 5 | -------------------------------------------------------------------------------- /tests/lint/empty.rkt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/empty.rkt -------------------------------------------------------------------------------- /tests/lint/empty.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/empty.rkt.out -------------------------------------------------------------------------------- /tests/lint/for-expressions.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define foos 4 | (for/list ([i (in-range 10)] 5 | [i (in-range 20)]) 6 | (define foo (* i 2)) 7 | foo)) 8 | 9 | (define foo 42) 10 | (define bar x) 11 | 12 | (for/list ()) 13 | (for/list ((a '(1 2 3))) a) 14 | 15 | (for/list ([x (in-range 10)] #:when y) 16 | (void)) 17 | 18 | (for/list ([x (in-range 10)] #:when y) 19 | x) 20 | 21 | (for/lists (a b c) 22 | ([x (in-range 10)] 23 | [y (in-range 10)] 24 | [z (in-range 10)]) 25 | (values 26 | (cons x a) 27 | (cons y b) 28 | (cons x a))) 29 | 30 | (for/lists (a b c #:result c) 31 | ([x (in-range 10)] 32 | [y (in-range 10)] 33 | [z (in-range 10)]) 34 | #:break z 35 | (values 36 | (cons x a) 37 | (cons y b) 38 | (cons x a))) 39 | 40 | (for/fold ([xs null]) 41 | ([x (in-list 10)]) 42 | #t) 43 | 44 | (for/fold ([xs null] #:result xs) 45 | ([x (in-list 10)]) 46 | #t) 47 | 48 | (for/fold ([xs null]) 49 | ([x (in-list 10)]) 50 | (cons x xs)) 51 | -------------------------------------------------------------------------------- /tests/lint/for-expressions.rkt.out: -------------------------------------------------------------------------------- 1 | /for-expressions.rkt:3:9:warning:identifier 'foos' is never used 2 | /for-expressions.rkt:5:15:error:identifier 'i' is already defined in another clause 3 | /for-expressions.rkt:9:9:warning:identifier 'foo' is never used 4 | /for-expressions.rkt:10:9:warning:identifier 'bar' is never used 5 | /for-expressions.rkt:12:1:error:for forms must contain at least one body expression 6 | /for-expressions.rkt:13:12:error:'for' clauses should be surrounded by square brackets 7 | /for-expressions.rkt:15:13:warning:identifier 'x' is never used 8 | /for-expressions.rkt:21:17:warning:identifier 'c' is never used 9 | /for-expressions.rkt:24:14:warning:identifier 'z' is never used 10 | /for-expressions.rkt:40:13:warning:identifier 'xs' is never used 11 | /for-expressions.rkt:41:13:warning:identifier 'x' is never used 12 | /for-expressions.rkt:45:13:warning:identifier 'x' is never used 13 | -------------------------------------------------------------------------------- /tests/lint/for-fold-clause-scope.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | display-leaf-timings) 5 | 6 | (define (display-leaf-timings tree) 7 | (for*/fold ([tree (hash)] 8 | #:result tree) 9 | ([(mod-path dependencies) (in-hash tree)] 10 | [dep (in-list dependencies)]) 11 | (hash-update tree dep (λ (dependents) (cons mod-path dependents)) null))) 12 | -------------------------------------------------------------------------------- /tests/lint/for-fold-clause-scope.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/for-fold-clause-scope.rkt.out -------------------------------------------------------------------------------- /tests/lint/function-varargs.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define (f x) 4 | (void)) 5 | 6 | (define (f . xs) 7 | (void)) 8 | 9 | (define (g a b) 10 | (void)) 11 | 12 | (define (h a b . xs) 13 | (void)) 14 | 15 | (define (i x [y 1] #:z z #:w [w z] . args) 16 | (cons w args)) 17 | -------------------------------------------------------------------------------- /tests/lint/function-varargs.rkt.out: -------------------------------------------------------------------------------- 1 | /function-varargs.rkt:3:10:warning:identifier 'f' is never used 2 | /function-varargs.rkt:3:12:warning:identifier 'x' is never used 3 | /function-varargs.rkt:6:10:error:identifier 'f' is already defined 4 | /function-varargs.rkt:6:14:warning:identifier 'xs' is never used 5 | /function-varargs.rkt:9:10:warning:identifier 'g' is never used 6 | /function-varargs.rkt:9:12:warning:identifier 'a' is never used 7 | /function-varargs.rkt:9:14:warning:identifier 'b' is never used 8 | /function-varargs.rkt:12:10:warning:identifier 'h' is never used 9 | /function-varargs.rkt:12:12:warning:identifier 'a' is never used 10 | /function-varargs.rkt:12:14:warning:identifier 'b' is never used 11 | /function-varargs.rkt:12:18:warning:identifier 'xs' is never used 12 | /function-varargs.rkt:15:10:warning:identifier 'i' is never used 13 | /function-varargs.rkt:15:12:warning:identifier 'x' is never used 14 | /function-varargs.rkt:15:15:warning:identifier 'y' is never used 15 | -------------------------------------------------------------------------------- /tests/lint/if-without-else.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (if #t 4 | (foo)) 5 | -------------------------------------------------------------------------------- /tests/lint/if-without-else.rkt.out: -------------------------------------------------------------------------------- 1 | /if-without-else.rkt:3:1:error:if expressions must contain one expression for the then-branch and another for the else-branch 2 | -------------------------------------------------------------------------------- /tests/lint/ignore-line.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define (f x y) ;; noqa 4 | (void)) 5 | -------------------------------------------------------------------------------- /tests/lint/ignore-line.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/ignore-line.rkt.out -------------------------------------------------------------------------------- /tests/lint/imports.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require (for-syntax racket/base 4 | syntax/parse) 5 | racket/contract) 6 | -------------------------------------------------------------------------------- /tests/lint/imports.rkt.out: -------------------------------------------------------------------------------- 1 | /imports.rkt:4:22:warning:prefer syntax/parse/pre if possible 2 | /imports.rkt:5:10:warning:prefer racket/contract/base if possible 3 | -------------------------------------------------------------------------------- /tests/lint/invalid-base-lang.rkt: -------------------------------------------------------------------------------- 1 | #lang racet/base 2 | -------------------------------------------------------------------------------- /tests/lint/invalid-base-lang.rkt.out: -------------------------------------------------------------------------------- 1 | /invalid-base-lang.rkt:1:1:error:standard-module-name-resolver: collection not found 2 | -------------------------------------------------------------------------------- /tests/lint/lambda-scope.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (lambda _ 4 | (void)) 5 | 6 | (lambda _ 7 | (void)) 8 | 9 | (lambda (a) 10 | a) 11 | 12 | (λ () 13 | (define a 1) 14 | (void)) 15 | 16 | (lambda (a b) 17 | a) 18 | 19 | (lambda (a b c) 20 | a) 21 | 22 | (lambda (a b . c) 23 | a) 24 | 25 | (lambda (a [b a] #:c c #:d [d #f] . xs) 26 | x) 27 | -------------------------------------------------------------------------------- /tests/lint/lambda-scope.rkt.out: -------------------------------------------------------------------------------- 1 | /lambda-scope.rkt:13:11:warning:identifier 'a' is never used 2 | /lambda-scope.rkt:16:12:warning:identifier 'b' is never used 3 | /lambda-scope.rkt:19:12:warning:identifier 'b' is never used 4 | /lambda-scope.rkt:19:14:warning:identifier 'c' is never used 5 | /lambda-scope.rkt:22:12:warning:identifier 'b' is never used 6 | /lambda-scope.rkt:22:16:warning:identifier 'c' is never used 7 | /lambda-scope.rkt:25:13:warning:identifier 'b' is never used 8 | /lambda-scope.rkt:25:22:warning:identifier 'c' is never used 9 | /lambda-scope.rkt:25:29:warning:identifier 'd' is never used 10 | /lambda-scope.rkt:25:37:warning:identifier 'xs' is never used 11 | -------------------------------------------------------------------------------- /tests/lint/lambda-varargs.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (lambda xs 4 | (void)) 5 | 6 | (lambda (a b . xs) 7 | (void)) 8 | -------------------------------------------------------------------------------- /tests/lint/lambda-varargs.rkt.out: -------------------------------------------------------------------------------- 1 | /lambda-varargs.rkt:3:9:warning:identifier 'xs' is never used 2 | /lambda-varargs.rkt:6:10:warning:identifier 'a' is never used 3 | /lambda-varargs.rkt:6:12:warning:identifier 'b' is never used 4 | /lambda-varargs.rkt:6:16:warning:identifier 'xs' is never used 5 | -------------------------------------------------------------------------------- /tests/lint/let-can-shadow.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define (a-page req) 4 | (let loop ([req req]) 5 | (send/suspend/dispatch 6 | (lambda (embed/url) 7 | (displayln req) 8 | (embed/url loop))))) 9 | -------------------------------------------------------------------------------- /tests/lint/let-can-shadow.rkt.out: -------------------------------------------------------------------------------- 1 | /let-can-shadow.rkt:3:10:warning:identifier 'a-page' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/let-with-parens.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (let ((x 1)) x) 4 | -------------------------------------------------------------------------------- /tests/lint/let-with-parens.rkt.out: -------------------------------------------------------------------------------- 1 | /let-with-parens.rkt:3:7:warning:bindings within a let should be surrounded by square brackets 2 | -------------------------------------------------------------------------------- /tests/lint/match-define.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/match) 4 | 5 | (struct foo (x y)) 6 | (define f (foo 1 2)) 7 | (match-define (foo a b) f) 8 | -------------------------------------------------------------------------------- /tests/lint/match-define.rkt.out: -------------------------------------------------------------------------------- 1 | /match-define.rkt:7:20:warning:identifier 'a' is never used 2 | /match-define.rkt:7:22:warning:identifier 'b' is never used 3 | -------------------------------------------------------------------------------- /tests/lint/match-lambda.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/match) 4 | 5 | (define foo 6 | (match-lambda 7 | [#t 8 | (define a 1) 9 | (+ a 2)])) 10 | 11 | (define bar 12 | (match-lambda 13 | [#t 14 | (define a 2) 15 | (+ a 1)])) 16 | -------------------------------------------------------------------------------- /tests/lint/match-lambda.rkt.out: -------------------------------------------------------------------------------- 1 | /match-lambda.rkt:5:9:warning:identifier 'foo' is never used 2 | /match-lambda.rkt:11:9:warning:identifier 'bar' is never used 3 | -------------------------------------------------------------------------------- /tests/lint/match.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/match) 4 | 5 | (define foo 1) 6 | 7 | (match foo 8 | [1 9 | (define foo 2) 10 | (define bar 3) 11 | (+ foo bar)] 12 | 13 | [2 14 | (define bar 4) 15 | bar]) 16 | 17 | (match foo 18 | [1 19 | (define bar 3) 20 | bar]) 21 | 22 | (match foo 23 | [null 1] 24 | [_ 2]) 25 | 26 | (match foo 27 | [(? eof-object?) void] 28 | [(or (? eof-object?) foo) (void)]) 29 | 30 | (match (list 1 2) 31 | [`(1 ,a) void] 32 | [`,b void]) 33 | -------------------------------------------------------------------------------- /tests/lint/match.rkt.out: -------------------------------------------------------------------------------- 1 | /match.rkt:9:12:warning:identifier 'foo' shadows an earlier binding 2 | /match.rkt:23:4:error:use '() for match pattern instead of null or empty 3 | /match.rkt:28:24:warning:identifier 'foo' is never used 4 | /match.rkt:31:9:warning:identifier 'a' is never used 5 | /match.rkt:32:6:warning:identifier 'b' is never used 6 | -------------------------------------------------------------------------------- /tests/lint/nested-modules.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (module+ test 4 | (provide a) 5 | 6 | (define b 1) 7 | (define b 1) 8 | 9 | (if 1 10 | (let () 11 | 2) 12 | (begin 13 | 3))) 14 | 15 | (define x 1) 16 | 17 | (module+ test 18 | (define x 2)) 19 | -------------------------------------------------------------------------------- /tests/lint/nested-modules.rkt.out: -------------------------------------------------------------------------------- 1 | /nested-modules.rkt:4:12:warning:identifier 'a' provided but not defined 2 | /nested-modules.rkt:6:11:warning:identifier 'b' is never used 3 | /nested-modules.rkt:7:11:error:identifier 'b' is already defined 4 | /nested-modules.rkt:9:3:warning:use a cond expression instead of nesting begin or let inside an if 5 | /nested-modules.rkt:15:9:warning:identifier 'x' is never used 6 | /nested-modules.rkt:18:11:warning:identifier 'x' is never used 7 | /nested-modules.rkt:18:11:warning:identifier 'x' shadows an earlier binding 8 | -------------------------------------------------------------------------------- /tests/lint/no-lang.rkt: -------------------------------------------------------------------------------- 1 | (displayln "hello") 2 | -------------------------------------------------------------------------------- /tests/lint/no-lang.rkt.out: -------------------------------------------------------------------------------- 1 | /no-lang.rkt:1:1:warning:missing module (#lang) declaration 2 | -------------------------------------------------------------------------------- /tests/lint/partial-lang.rkt: -------------------------------------------------------------------------------- 1 | #lang 2 | -------------------------------------------------------------------------------- /tests/lint/partial-lang.rkt.out: -------------------------------------------------------------------------------- 1 | /partial-lang.rkt:1:1:error:expected a single space after `#lang` 2 | -------------------------------------------------------------------------------- /tests/lint/provided-all-defined.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (all-defined-out)) 4 | 5 | (define foo 'bar) 6 | -------------------------------------------------------------------------------- /tests/lint/provided-all-defined.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/provided-all-defined.rkt.out -------------------------------------------------------------------------------- /tests/lint/provided-but-not-defined.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | g 5 | 6 | (contract-out 7 | [a any/c] 8 | [y exact-integer?]) 9 | 10 | (rename-out [h i] 11 | [j k] 12 | [l 1]) 13 | x) 14 | 15 | (define x 42) 16 | (define y 24) 17 | -------------------------------------------------------------------------------- /tests/lint/provided-but-not-defined.rkt.out: -------------------------------------------------------------------------------- 1 | /provided-but-not-defined.rkt:4:2:warning:identifier 'g' provided but not defined 2 | /provided-but-not-defined.rkt:7:4:warning:identifier 'a' provided but not defined 3 | /provided-but-not-defined.rkt:10:15:warning:identifier 'h' provided but not defined 4 | /provided-but-not-defined.rkt:11:15:warning:identifier 'j' provided but not defined 5 | /provided-but-not-defined.rkt:12:15:warning:identifier 'l' provided but not defined 6 | /provided-but-not-defined.rkt:12:17:error:not an identifier 7 | -------------------------------------------------------------------------------- /tests/lint/punted-bindings.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | (contract-out 5 | [a an-a?])) 6 | 7 | (define a 'a) 8 | 9 | (define (an-a? v) 10 | (eq? v 'a)) 11 | 12 | (lambda () 13 | (show "hello") 14 | 15 | (let () 16 | (define (show s) 17 | (displayln s)))) 18 | 19 | (lambda _ 20 | (show "hello") 21 | 22 | (define (show s) 23 | (displayln s))) 24 | 25 | (define (breadcrumbs . crumbs) 26 | (haml (:ul.breadcrums (@ crumbs)))) 27 | 28 | (define (breadcrumb uri label) 29 | (void)) 30 | 31 | (breadcrumbs 32 | (breadcrumb)) 33 | -------------------------------------------------------------------------------- /tests/lint/punted-bindings.rkt.out: -------------------------------------------------------------------------------- 1 | /punted-bindings.rkt:16:14:warning:identifier 'show' is never used 2 | /punted-bindings.rkt:22:12:warning:identifier 'show' is never used 3 | /punted-bindings.rkt:28:21:warning:identifier 'uri' is never used 4 | /punted-bindings.rkt:28:25:warning:identifier 'label' is never used 5 | -------------------------------------------------------------------------------- /tests/lint/rackunit-test-case.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require rackunit) 4 | 5 | (define expected 42) 6 | (define bar 1) 7 | 8 | (test-suite 9 | "a" 10 | 11 | (test-case "b" 12 | (define foo 42) 13 | (check-equal? foo expected)) 14 | 15 | (test-case "c" 16 | (define foo 43) 17 | (check-not-equal? foo expected))) 18 | -------------------------------------------------------------------------------- /tests/lint/rackunit-test-case.rkt.out: -------------------------------------------------------------------------------- 1 | /rackunit-test-case.rkt:6:9:warning:identifier 'bar' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/require-combine-in.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require (combine-in)) 4 | 5 | (require racket/string 6 | (prefix-in combined: (combine-in threading))) 7 | 8 | (require (prefix-in combined: (combine-in threading)) 9 | racket/string) 10 | 11 | (require (prefix-in combined: (combine-in threading 12 | racket/base)) 13 | racket/string) 14 | -------------------------------------------------------------------------------- /tests/lint/require-combine-in.rkt.out: -------------------------------------------------------------------------------- 1 | /require-combine-in.rkt:3:10:warning:empty combine-in 2 | /require-combine-in.rkt:9:10:warning:require "racket/string" should come before "threading" 3 | /require-combine-in.rkt:12:43:warning:require "racket/base" should come before "threading" 4 | /require-combine-in.rkt:13:10:warning:require "racket/string" should come before "threading" 5 | -------------------------------------------------------------------------------- /tests/lint/require-sorted-local.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/file 4 | racket/format 5 | racket/string 6 | "a.rkt" 7 | "b.rkt") 8 | -------------------------------------------------------------------------------- /tests/lint/require-sorted-local.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/require-sorted-local.rkt.out -------------------------------------------------------------------------------- /tests/lint/require-sorted.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/file 4 | racket/format 5 | racket/string) 6 | -------------------------------------------------------------------------------- /tests/lint/require-sorted.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/require-sorted.rkt.out -------------------------------------------------------------------------------- /tests/lint/require-submod.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require koyo/database 4 | (submod koyo/console dev)) 5 | -------------------------------------------------------------------------------- /tests/lint/require-submod.rkt.out: -------------------------------------------------------------------------------- 1 | /require-submod.rkt:4:10:warning:require "koyo/console" should come before "koyo/database" 2 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-for-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string 4 | (for-syntax racket/base) 5 | racket/format 6 | racket/file 7 | "b.rkt" 8 | "a.rkt") 9 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-for-syntax.rkt.out: -------------------------------------------------------------------------------- 1 | /require-unsorted-for-syntax.rkt:4:10:warning:require (for-syntax ...) should come before all others 2 | /require-unsorted-for-syntax.rkt:6:10:warning:require "racket/file" should come before "racket/format" 3 | /require-unsorted-for-syntax.rkt:8:10:warning:require "a.rkt" should come before "b.rkt" 4 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-local-1.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string 4 | racket/format 5 | racket/file 6 | "b.rkt" 7 | "a.rkt") 8 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-local-1.rkt.out: -------------------------------------------------------------------------------- 1 | /require-unsorted-local-1.rkt:4:10:warning:require "racket/format" should come before "racket/string" 2 | /require-unsorted-local-1.rkt:5:10:warning:require "racket/file" should come before "racket/format" 3 | /require-unsorted-local-1.rkt:7:10:warning:require "a.rkt" should come before "b.rkt" 4 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-local-2.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string 4 | "b.rkt" 5 | racket/format 6 | racket/file 7 | "a.rkt") 8 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-local-2.rkt.out: -------------------------------------------------------------------------------- 1 | /require-unsorted-local-2.rkt:4:10:warning:require "b.rkt" should come after "racket/format" 2 | /require-unsorted-local-2.rkt:6:10:warning:require "racket/file" should come before "racket/format" 3 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-prefix-in.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string 4 | (prefix-in f: racket/format) 5 | racket/file 6 | "b.rkt" 7 | "a.rkt") 8 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted-prefix-in.rkt.out: -------------------------------------------------------------------------------- 1 | /require-unsorted-prefix-in.rkt:4:10:warning:require "racket/format" should come before "racket/string" 2 | /require-unsorted-prefix-in.rkt:5:10:warning:require "racket/file" should come before "racket/format" 3 | /require-unsorted-prefix-in.rkt:7:10:warning:require "a.rkt" should come before "b.rkt" 4 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string 4 | racket/format 5 | racket/file) 6 | -------------------------------------------------------------------------------- /tests/lint/require-unsorted.rkt.out: -------------------------------------------------------------------------------- 1 | /require-unsorted.rkt:4:10:warning:require "racket/format" should come before "racket/string" 2 | /require-unsorted.rkt:5:10:warning:require "racket/file" should come before "racket/format" 3 | -------------------------------------------------------------------------------- /tests/lint/shadow-arg.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define method "GET") 4 | 5 | (define (request [method method]) 6 | (void)) 7 | -------------------------------------------------------------------------------- /tests/lint/shadow-arg.rkt.out: -------------------------------------------------------------------------------- 1 | /shadow-arg.rkt:5:10:warning:identifier 'request' is never used 2 | /shadow-arg.rkt:5:19:warning:identifier 'method' is never used 3 | /shadow-arg.rkt:5:19:warning:identifier 'method' shadows an earlier binding 4 | -------------------------------------------------------------------------------- /tests/lint/struct-fields.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | user 5 | user-username) 6 | 7 | (struct user (username password)) 8 | -------------------------------------------------------------------------------- /tests/lint/struct-fields.rkt.out: -------------------------------------------------------------------------------- 1 | /struct-fields.rkt:7:24:warning:identifier 'user-password' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/struct-methods.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/generic 4 | racket/match) 5 | 6 | (define-generics store 7 | {store-ref store}) 8 | 9 | (struct memory-store (ttl) 10 | #:methods gen:store 11 | [(define (store-ref s) 12 | (match-define (memory-store ttl) s) 13 | (void))]) 14 | -------------------------------------------------------------------------------- /tests/lint/struct-methods.rkt.out: -------------------------------------------------------------------------------- 1 | /struct-methods.rkt:12:34:warning:identifier 'ttl' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/struct-mutable-field.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string) 4 | 5 | (provide 6 | user? 7 | user 8 | user-username 9 | set-user-username! 10 | user-password 11 | set-user-password! 12 | user-password-hash 13 | set-user-password-hash!) 14 | 15 | (struct user 16 | (username [password-hash #:mutable]) 17 | #:transparent) 18 | -------------------------------------------------------------------------------- /tests/lint/struct-mutable-field.rkt.out: -------------------------------------------------------------------------------- 1 | /struct-mutable-field.rkt:9:2:warning:identifier 'set-user-username!' provided but not defined 2 | /struct-mutable-field.rkt:10:2:warning:identifier 'user-password' provided but not defined 3 | /struct-mutable-field.rkt:11:2:warning:identifier 'set-user-password!' provided but not defined 4 | -------------------------------------------------------------------------------- /tests/lint/struct-mutable.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string) 4 | 5 | (provide 6 | user? 7 | user 8 | user-username 9 | set-user-username! 10 | user-password 11 | set-user-password! 12 | user-password-hash 13 | set-user-password-hash!) 14 | 15 | (struct user 16 | (username password-hash) 17 | #:transparent 18 | #:mutable) 19 | -------------------------------------------------------------------------------- /tests/lint/struct-mutable.rkt.out: -------------------------------------------------------------------------------- 1 | /struct-mutable.rkt:10:2:warning:identifier 'user-password' provided but not defined 2 | /struct-mutable.rkt:11:2:warning:identifier 'set-user-password!' provided but not defined 3 | -------------------------------------------------------------------------------- /tests/lint/struct-out.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (struct-out voice)) 4 | (struct voice (languages name sample-rate gender) #:transparent) 5 | -------------------------------------------------------------------------------- /tests/lint/struct-out.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/struct-out.rkt.out -------------------------------------------------------------------------------- /tests/lint/struct-plus-plus.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string) 4 | 5 | (provide 6 | user 7 | user-username 8 | set-user-username 9 | user-password-hash 10 | set-user-password 11 | set-user-password-hash) 12 | 13 | (struct++ user 14 | ([username non-empty-string?] 15 | [password-hash bytes?]) 16 | #:transparent) 17 | -------------------------------------------------------------------------------- /tests/lint/struct-plus-plus.rkt.out: -------------------------------------------------------------------------------- 1 | /struct-plus-plus.rkt:10:2:warning:identifier 'set-user-password' provided but not defined 2 | /struct-plus-plus.rkt:13:11:warning:identifier 'user++' is never used 3 | -------------------------------------------------------------------------------- /tests/lint/struct.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/string) 4 | 5 | (provide 6 | user? 7 | user 8 | user-username 9 | user-password 10 | user-password-hash) 11 | 12 | (struct user 13 | (username password-hash) 14 | #:transparent) 15 | 16 | (define u (user "a" "")) 17 | (set-user-username! u "b") 18 | -------------------------------------------------------------------------------- /tests/lint/struct.rkt.out: -------------------------------------------------------------------------------- 1 | /struct.rkt:9:2:warning:identifier 'user-password' provided but not defined 2 | -------------------------------------------------------------------------------- /tests/lint/submodule.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (module unsafe racket/base 4 | (provide (all-defined-out)) 5 | (define x 42)) 6 | 7 | (module foo racket/base 8 | (provide y) 9 | (define x 42) 10 | (define (f x) 11 | (println x))) 12 | 13 | (module bar racket/base 14 | (require racket/string) 15 | (provide non-empty-string?)) 16 | 17 | (provide x) 18 | -------------------------------------------------------------------------------- /tests/lint/submodule.rkt.out: -------------------------------------------------------------------------------- 1 | /submodule.rkt:8:12:warning:identifier 'y' provided but not defined 2 | /submodule.rkt:10:14:warning:identifier 'x' shadows an earlier binding 3 | /submodule.rkt:15:12:warning:identifier 'non-empty-string?' provided but not defined 4 | /submodule.rkt:17:10:warning:identifier 'x' provided but not defined 5 | -------------------------------------------------------------------------------- /tests/lint/syntax-error.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | ( 4 | -------------------------------------------------------------------------------- /tests/lint/syntax-error.rkt.out: -------------------------------------------------------------------------------- 1 | /syntax-error.rkt:3:1:error:expected a `)` to close `(` 2 | -------------------------------------------------------------------------------- /tests/lint/tilde-ids.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide ~foo) 4 | (define ~foo 1) 5 | -------------------------------------------------------------------------------- /tests/lint/tilde-ids.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/tilde-ids.rkt.out -------------------------------------------------------------------------------- /tests/lint/underscores-dont-shadow.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define _ 1) 4 | 5 | (define (f _) 6 | 2) 7 | 8 | (f 3) 9 | -------------------------------------------------------------------------------- /tests/lint/underscores-dont-shadow.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/underscores-dont-shadow.rkt.out -------------------------------------------------------------------------------- /tests/lint/unused-bindings.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | h) 5 | 6 | (define (f x) 7 | (define y 0) 8 | 1) 9 | 10 | (f 1) 11 | 12 | (define ((g x) y) 13 | x) 14 | 15 | (define (h) 16 | (displayln "I'm provided!")) 17 | 18 | (let ([x 1] 19 | [y x]) 20 | y) 21 | 22 | (let ([x 1] 23 | [y 2]) 24 | 3) 25 | 26 | (define (i #:ok? ok?) 27 | #f) 28 | 29 | (define (j #:ok? [ok? #f]) 30 | #t) 31 | 32 | (cond 33 | [#t 34 | (define z 1) 35 | #f]) 36 | 37 | (define _foo 42) 38 | -------------------------------------------------------------------------------- /tests/lint/unused-bindings.rkt.out: -------------------------------------------------------------------------------- 1 | /unused-bindings.rkt:6:12:warning:identifier 'x' is never used 2 | /unused-bindings.rkt:7:11:warning:identifier 'y' is never used 3 | /unused-bindings.rkt:12:11:warning:identifier 'g' is never used 4 | /unused-bindings.rkt:12:16:warning:identifier 'y' is never used 5 | /unused-bindings.rkt:22:8:warning:identifier 'x' is never used 6 | /unused-bindings.rkt:23:8:warning:identifier 'y' is never used 7 | /unused-bindings.rkt:26:10:warning:identifier 'i' is never used 8 | /unused-bindings.rkt:26:18:warning:identifier 'ok?' is never used 9 | /unused-bindings.rkt:29:10:warning:identifier 'j' is never used 10 | /unused-bindings.rkt:29:19:warning:identifier 'ok?' is never used 11 | /unused-bindings.rkt:32:1:warning:this cond expression does not have an else clause 12 | /unused-bindings.rkt:34:12:warning:identifier 'z' is never used 13 | -------------------------------------------------------------------------------- /tests/lint/use-before-define.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (define (f x) 4 | (define y (g x)) 5 | y) 6 | 7 | (define (g x) 8 | (add 1 x)) 9 | -------------------------------------------------------------------------------- /tests/lint/use-before-define.rkt.out: -------------------------------------------------------------------------------- 1 | /use-before-define.rkt:3:10:warning:identifier 'f' is never used 2 | -------------------------------------------------------------------------------- /tests/lint/when-unless.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (when #t 4 | (define-values (a b) 5 | (values 1 2)) 6 | (println (list a b))) 7 | 8 | (unless #f 9 | (define-values (c d) 10 | (values 3 4)) 11 | (println (list c d))) 12 | 13 | (define a 42) 14 | (define b 43) 15 | (define c 44) 16 | (define d 45) 17 | (list a b c d) 18 | -------------------------------------------------------------------------------- /tests/lint/when-unless.rkt.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdanp/racket-review/8059cf48e324625a4e9439620a1b24ad73f5b3b2/tests/lint/when-unless.rkt.out --------------------------------------------------------------------------------