Extract Semgrep JavaScript vardef scanner
ober
d447e027fbd0a73c96b33388cd53a43268806003
--- a/SEMGREP_JERBOA_IMPLEMENTATION.md +++ b/SEMGREP_JERBOA_IMPLEMENTATION.md @@ -475,6 +475,8 @@ Completed in the repo: `src/semgrep/engine/text-support.ss` - extracted XML/YAML special-case scanners into `src/semgrep/engine/markup-scan.ss` + - extracted JavaScript `vardef_assign` text scanning into + `src/semgrep/engine/js-vardef-scan.ss` Validation at this checkpoint: new file mode 100644 --- /dev/null +++ b/lib/semgrep/engine/js-vardef-scan.sls @@ -0,0 +1,216 @@ +#!chezscheme +;;; Generated by jerbuild — DO NOT EDIT +;;; Source: src/semgrep/engine/js-vardef-scan.ss + +(library (semgrep engine js-vardef-scan) + (export scan-javascript-vardef-assign-pattern) + (import + (except (chezscheme) make-hash-table hash-table? sort sort! + printf fprintf format path-extension path-absolute? + with-input-from-string with-output-to-string iota \x31;+ + \x31;- partition make-date make-time meta atom?) + (except (jerboa prelude) meta atom?) (std regex) + (semgrep rule) (semgrep engine generic-scan) + (semgrep engine regex-support) + (semgrep engine text-support)) + (def (alist-ref/default xs key default) + (let ([found (assoc key xs)]) + (if found (cdr found) default))) + (def (rule-option-entry rule key) + (let ([options (rule-options rule)]) + (or (assoc key options) + (and (string? key) (assoc (string->symbol key) options)) + (and (symbol? key) (assoc (symbol->string key) options))))) + (def (rule-option-explicit-false? rule key) + (let ([found (rule-option-entry rule key)]) + (and found (not (cdr found))))) + (def (sg-string-prefix? prefix s) + (let ([prefix-len (string-length prefix)] + [len (string-length s)]) + (and (<= prefix-len len) + (string=? (substring s 0 prefix-len) prefix)))) + (def (string-find-substring s needle) + (let ([len (string-length s)]) + (let loop ([i 0]) + (cond + [(> i len) #f] + [(substring-at? s needle i) i] + [else (loop (+ i 1))])))) + (def (identifier-char? ch) + (or (char-alphabetic? ch) + (char-numeric? ch) + (char=? ch #\_) + (char=? ch #\$))) + (def (skip-horizontal-backward source i) + (let loop ([j i]) + (if (and (>= j 0) + (let ([ch (string-ref source j)]) + (or (char=? ch #\space) (char=? ch #\tab)))) + (loop (- j 1)) + j))) + (def (javascript-vardef-assign-pattern-kind pattern) + (let ([trimmed (string-trim pattern)]) + (cond + [(sg-string-prefix? "<..." trimmed) 'expression-ellipsis] + [(string-find-substring pattern "\n") 'statement-sequence] + [else 'plain]))) + (def (javascript-vardef-assign-pattern-spec pattern) + (let* ([match (re-search + (re "([A-Za-z_$][A-Za-z0-9_$]*)[ \\t]*=[ \\t]*([^;>\\n]+)") + pattern + 0)] + [name (and match (re-match-group match 1))] + [value0 (and match (string-trim (re-match-group match 2)))] + [ellipsis (and value0 (string-find-substring value0 "..."))] + [value (and value0 + (if ellipsis + (string-trim (substring value0 0 ellipsis)) + value0))]) + (and name + value + (not (string-find-substring name "$")) + (not (string-find-substring value "$")) + (list + (cons 'name name) + (cons 'value value) + (cons + 'kind + (javascript-vardef-assign-pattern-kind pattern)))))) + (def (identifier-boundary-before? source index) + (or (= index 0) + (not (identifier-char? (string-ref source (- index 1)))))) + (def (identifier-boundary-after? source index) + (or (>= index (string-length source)) + (not (identifier-char? (string-ref source index))))) + (def (previous-word-before source index) + (let ([before (skip-horizontal-backward + source + (- index 1))]) + (and (>= before 0) + (identifier-char? (string-ref source before)) + (let loop ([start before]) + (if (and (> start 0) + (identifier-char? + (string-ref source (- start 1)))) + (loop (- start 1)) + (substring source start (+ before 1))))))) + (def (javascript-declaration-keyword? word) + (or (string=? word "var") + (string=? word "let") + (string=? word "const"))) + (def (javascript-vardef-assignment-preceded-by-declaration? + source + start) + (let ([word (previous-word-before source start)]) + (and word (javascript-declaration-keyword? word)))) + (def (javascript-vardef-assignment-finding rule path source + start end) + (finding-for-range-with-bindings rule path source start end + '())) + (def (scan-javascript-vardef-actual-assignments + rule + path + source + spec) + (let* ([name (alist-ref/default spec 'name "")] + [value (alist-ref/default spec 'value "")] + [rx (re (string-append + (regex-escape-string name) + "[ \\t]*=[ \\t]*" + (regex-escape-string value)))] + [len (string-length source)]) + (let loop ([start 0] [acc '()]) + (let ([match (re-search rx source start)]) + (if (not match) + (reverse acc) + (let* ([match-start (re-match-start match)] + [match-end (re-match-end match)] + [finding (and (identifier-boundary-before? + source + match-start) + (identifier-boundary-after? + source + (+ match-start + (string-length name))) + (identifier-boundary-after? + source + match-end) + (not (javascript-vardef-assignment-preceded-by-declaration? + source + match-start)) + (javascript-vardef-assignment-finding rule path source match-start + match-end))] + [next (max (+ match-start 1) match-end)]) + (loop next (if finding (cons finding acc) acc)))))))) + (def (javascript-vardef-declaration-range source spec match) + (let* ([kind (alist-ref/default spec 'kind 'plain)] + [name (alist-ref/default spec 'name "")] + [decl-start (re-match-start match)] + [match-end (re-match-end match)] + [name-start (string-find-substring-from + source + name + decl-start)] + [start (if (eq? kind 'statement-sequence) + decl-start + name-start)]) + (and start (cons start match-end)))) + (def (scan-javascript-vardef-declarations + rule + path + source + spec) + (let* ([name (alist-ref/default spec 'name "")] + [value (alist-ref/default spec 'value "")] + [rx (re (string-append + "(var|let|const)[ \\t]+" + (regex-escape-string name) + "[ \\t]*=[ \\t]*" + (regex-escape-string value)))] + [len (string-length source)]) + (let loop ([start 0] [acc '()]) + (let ([match (re-search rx source start)]) + (if (not match) + (reverse acc) + (let* ([range (and (identifier-boundary-before? + source + (re-match-start match)) + (identifier-boundary-after? + source + (re-match-end match)) + (javascript-vardef-declaration-range + source + spec + match))] + [finding (and range + (javascript-vardef-assignment-finding rule path source (car range) + (cdr range)))] + [next (max (+ (re-match-start match) 1) + (re-match-end match))]) + (loop next (if finding (cons finding acc) acc)))))))) + (def (scan-javascript-vardef-assign-pattern + rule + path + source + pattern) + (let ([option (rule-option-entry rule "vardef_assign")]) + (and option + (let ([spec (javascript-vardef-assign-pattern-spec + pattern)]) + (and spec + (let ([assignments (scan-javascript-vardef-actual-assignments + rule + path + source + spec)]) + (if (rule-option-explicit-false? + rule + "vardef_assign") + assignments + (append + (scan-javascript-vardef-declarations + rule + path + source + spec) + assignments))))))))) --- a/lib/semgrep/scan.sls +++ b/lib/semgrep/scan.sls @@ -17,7 +17,8 @@ (tree-sitter tree-sitter) (semgrep lang) (semgrep rule) (semgrep result) (semgrep result builders) (semgrep result extras) (semgrep result findings) - (semgrep engine generic-scan) (semgrep engine markup-scan) + (semgrep engine generic-scan) + (semgrep engine js-vardef-scan) (semgrep engine markup-scan) (semgrep engine regex-scan) (semgrep engine rule-plan) (semgrep engine regex-support) (semgrep engine text-support) (semgrep rule parse-rule) (semgrep parse parse-target) @@ -151,6 +152,24 @@ (string=? (substring text (- len 2) len) "()")) (substring text 0 (- len 2)) text))) + (def (identifier-boundary-before? source index) + (or (= index 0) + (not (identifier-char? (string-ref source (- index 1)))))) + (def (identifier-boundary-after? source index) + (or (>= index (string-length source)) + (not (identifier-char? (string-ref source index))))) + (def (previous-word-before source index) + (let ([before (skip-horizontal-backward + source + (- index 1))]) + (and (>= before 0) + (identifier-char? (string-ref source before)) + (let loop ([start before]) + (if (and (> start 0) + (identifier-char? + (string-ref source (- start 1)))) + (loop (- start 1)) + (substring source start (+ before 1))))))) (def (decorated-field-pattern-spec pattern) (let* ([trimmed (string-trim pattern)] [len (string-length trimmed)]) @@ -459,172 +478,6 @@ (loop next (if finding (cons finding acc) acc)))))))))) - (def (javascript-vardef-assign-pattern-kind pattern) - (let ([trimmed (string-trim pattern)]) - (cond - [(sg-string-prefix? "<..." trimmed) 'expression-ellipsis] - [(string-find-substring pattern "\n") 'statement-sequence] - [else 'plain]))) - (def (javascript-vardef-assign-pattern-spec pattern) - (let* ([match (re-search - (re "([A-Za-z_$][A-Za-z0-9_$]*)[ \\t]*=[ \\t]*([^;>\\n]+)") - pattern - 0)] - [name (and match (re-match-group match 1))] - [value0 (and match (string-trim (re-match-group match 2)))] - [ellipsis (and value0 (string-find-substring value0 "..."))] - [value (and value0 - (if ellipsis - (string-trim (substring value0 0 ellipsis)) - value0))]) - (and name - value - (not (string-find-substring name "$")) - (not (string-find-substring value "$")) - (list - (cons 'name name) - (cons 'value value) - (cons - 'kind - (javascript-vardef-assign-pattern-kind pattern)))))) - (def (identifier-boundary-before? source index) - (or (= index 0) - (not (identifier-char? (string-ref source (- index 1)))))) - (def (identifier-boundary-after? source index) - (or (>= index (string-length source)) - (not (identifier-char? (string-ref source index))))) - (def (previous-word-before source index) - (let ([before (skip-horizontal-backward - source - (- index 1))]) - (and (>= before 0) - (identifier-char? (string-ref source before)) - (let loop ([start before]) - (if (and (> start 0) - (identifier-char? - (string-ref source (- start 1)))) - (loop (- start 1)) - (substring source start (+ before 1))))))) - (def (javascript-declaration-keyword? word) - (or (string=? word "var") - (string=? word "let") - (string=? word "const"))) - (def (javascript-vardef-assignment-preceded-by-declaration? - source - start) - (let ([word (previous-word-before source start)]) - (and word (javascript-declaration-keyword? word)))) - (def (javascript-vardef-assignment-finding rule path source - start end) - (finding-for-range-with-bindings rule path source start end - '())) - (def (scan-javascript-vardef-actual-assignments - rule - path - source - spec) - (let* ([name (alist-ref/default spec 'name "")] - [value (alist-ref/default spec 'value "")] - [rx (re (string-append - (regex-escape-string name) - "[ \\t]*=[ \\t]*" - (regex-escape-string value)))] - [len (string-length source)]) - (let loop ([start 0] [acc '()]) - (let ([match (re-search rx source start)]) - (if (not match) - (reverse acc) - (let* ([match-start (re-match-start match)] - [match-end (re-match-end match)] - [finding (and (identifier-boundary-before? - source - match-start) - (identifier-boundary-after? - source - (+ match-start - (string-length name))) - (identifier-boundary-after? - source - match-end) - (not (javascript-vardef-assignment-preceded-by-declaration? - source - match-start)) - (javascript-vardef-assignment-finding rule path source match-start - match-end))] - [next (max (+ match-start 1) match-end)]) - (loop next (if finding (cons finding acc) acc)))))))) - (def (javascript-vardef-declaration-range source spec match) - (let* ([kind (alist-ref/default spec 'kind 'plain)] - [name (alist-ref/default spec 'name "")] - [decl-start (re-match-start match)] - [match-end (re-match-end match)] - [name-start (string-find-substring-from - source - name - decl-start)] - [start (if (eq? kind 'statement-sequence) - decl-start - name-start)]) - (and start (cons start match-end)))) - (def (scan-javascript-vardef-declarations - rule - path - source - spec) - (let* ([name (alist-ref/default spec 'name "")] - [value (alist-ref/default spec 'value "")] - [rx (re (string-append - "(var|let|const)[ \\t]+" - (regex-escape-string name) - "[ \\t]*=[ \\t]*" - (regex-escape-string value)))] - [len (string-length source)]) - (let loop ([start 0] [acc '()]) - (let ([match (re-search rx source start)]) - (if (not match) - (reverse acc) - (let* ([range (and (identifier-boundary-before? - source - (re-match-start match)) - (identifier-boundary-after? - source - (re-match-end match)) - (javascript-vardef-declaration-range - source - spec - match))] - [finding (and range - (javascript-vardef-assignment-finding rule path source (car range) - (cdr range)))] - [next (max (+ (re-match-start match) 1) - (re-match-end match))]) - (loop next (if finding (cons finding acc) acc)))))))) - (def (scan-javascript-vardef-assign-pattern - rule - path - source - pattern) - (let ([option (rule-option-entry rule "vardef_assign")]) - (and option - (let ([spec (javascript-vardef-assign-pattern-spec - pattern)]) - (and spec - (let ([assignments (scan-javascript-vardef-actual-assignments - rule - path - source - spec)]) - (if (rule-option-explicit-false? - rule - "vardef_assign") - assignments - (append - (scan-javascript-vardef-declarations - rule - path - source - spec) - assignments)))))))) (def (json-key-value-pattern-spec pattern) (let* ([match (re-search (re "\\$([A-Za-z_][A-Za-z0-9_]*)[ \\t]*:[ \\t]*([^,}\\n]+)") --- a/src/.jerbuild-hashes +++ b/src/.jerbuild-hashes @@ -1,4 +1,7 @@ (("src/semgrep/output/sarif.ss" . "E935456E4B1921FB") + ("src/semgrep/engine/js-vardef-scan.ss" + . + "BD82BDDFEDD7242D") ("src/semgrep/result.ss" . "22D23E40B49BA529") ("src/semgrep/engine/regex-scan.ss" . "D75414F0AEFE2F66") ("src/semgrep/engine/rule-plan.ss" . "6631789392AB5F80") @@ -13,7 +16,7 @@ ("src/semgrep/lang.ss" . "6982E07679D20836") ("src/semgrep/parse/parse-target.ss" . "97AA8FFEB12736DA") ("src/semgrep/result/extras.ss" . "DF0B3AAE2BAEB5D") - ("src/semgrep/scan.ss" . "FC88B6BCF91FDD04") + ("src/semgrep/scan.ss" . "D0040BF672A86721") ("src/semgrep/engine/generic-scan.ss" . "F69D0ACD0DD62610") ("src/semgrep/schema/lang.ss" . "CAE2CA859C9A9FD0") ("src/semgrep/rule.ss" . "E12C108153C181FA") new file mode 100644 --- /dev/null +++ b/src/semgrep/engine/js-vardef-scan.ss @@ -0,0 +1,212 @@ +(export + scan-javascript-vardef-assign-pattern) + +(import (except (jerboa prelude) meta atom?) + (std regex) + (semgrep rule) + (semgrep engine generic-scan) + (semgrep engine regex-support) + (semgrep engine text-support)) + +(def (alist-ref/default xs key default) + (let ([found (assoc key xs)]) + (if found (cdr found) default))) + +(def (rule-option-entry rule key) + (let ([options (rule-options rule)]) + (or (assoc key options) + (and (string? key) + (assoc (string->symbol key) options)) + (and (symbol? key) + (assoc (symbol->string key) options))))) + +(def (rule-option-explicit-false? rule key) + (let ([found (rule-option-entry rule key)]) + (and found (not (cdr found))))) + +(def (sg-string-prefix? prefix s) + (let ([prefix-len (string-length prefix)] + [len (string-length s)]) + (and (<= prefix-len len) + (string=? (substring s 0 prefix-len) prefix)))) + +(def (string-find-substring s needle) + (let ([len (string-length s)]) + (let loop ([i 0]) + (cond + [(> i len) #f] + [(substring-at? s needle i) i] + [else (loop (+ i 1))])))) + +(def (identifier-char? ch) + (or (char-alphabetic? ch) + (char-numeric? ch) + (char=? ch #\_) + (char=? ch #\$))) + +(def (skip-horizontal-backward source i) + (let loop ([j i]) + (if (and (>= j 0) + (let ([ch (string-ref source j)]) + (or (char=? ch #\space) + (char=? ch #\tab)))) + (loop (- j 1)) + j))) + +(def (javascript-vardef-assign-pattern-kind pattern) + (let ([trimmed (string-trim pattern)]) + (cond + [(sg-string-prefix? "<..." trimmed) 'expression-ellipsis] + [(string-find-substring pattern "\n") 'statement-sequence] + [else 'plain]))) + +(def (javascript-vardef-assign-pattern-spec pattern) + (let* ([match (re-search + (re "([A-Za-z_$][A-Za-z0-9_$]*)[ \\t]*=[ \\t]*([^;>\\n]+)") + pattern + 0)] + [name (and match (re-match-group match 1))] + [value0 (and match (string-trim (re-match-group match 2)))] + [ellipsis (and value0 (string-find-substring value0 "..."))] + [value (and value0 + (if ellipsis + (string-trim (substring value0 0 ellipsis)) + value0))]) + (and name + value + (not (string-find-substring name "$")) + (not (string-find-substring value "$")) + (list (cons 'name name) + (cons 'value value) + (cons 'kind + (javascript-vardef-assign-pattern-kind pattern)))))) + +(def (identifier-boundary-before? source index) + (or (= index 0) + (not (identifier-char? (string-ref source (- index 1)))))) + +(def (identifier-boundary-after? source index) + (or (>= index (string-length source)) + (not (identifier-char? (string-ref source index))))) + +(def (previous-word-before source index) + (let ([before (skip-horizontal-backward source (- index 1))]) + (and (>= before 0) + (identifier-char? (string-ref source before)) + (let loop ([start before]) + (if (and (> start 0) + (identifier-char? (string-ref source (- start 1)))) + (loop (- start 1)) + (substring source start (+ before 1))))))) + +(def (javascript-declaration-keyword? word) + (or (string=? word "var") + (string=? word "let") + (string=? word "const"))) + +(def (javascript-vardef-assignment-preceded-by-declaration? source start) + (let ([word (previous-word-before source start)]) + (and word (javascript-declaration-keyword? word)))) + +(def (javascript-vardef-assignment-finding rule path source start end) + (finding-for-range-with-bindings rule path source start end '())) + +(def (scan-javascript-vardef-actual-assignments rule path source spec) + (let* ([name (alist-ref/default spec 'name "")] + [value (alist-ref/default spec 'value "")] + [rx (re (string-append + (regex-escape-string name) + "[ \\t]*=[ \\t]*" + (regex-escape-string value)))] + [len (string-length source)]) + (let loop ([start 0] [acc '()]) + (let ([match (re-search rx source start)]) + (if (not match) + (reverse acc) + (let* ([match-start (re-match-start match)] + [match-end (re-match-end match)] + [finding + (and (identifier-boundary-before? source match-start) + (identifier-boundary-after? + source + (+ match-start (string-length name))) + (identifier-boundary-after? source match-end) + (not (javascript-vardef-assignment-preceded-by-declaration? + source + match-start)) + (javascript-vardef-assignment-finding + rule + path + source + match-start + match-end))] + [next (max (+ match-start 1) match-end)]) + (loop next (if finding (cons finding acc) acc)))))))) + +(def (javascript-vardef-declaration-range source spec match) + (let* ([kind (alist-ref/default spec 'kind 'plain)] + [name (alist-ref/default spec 'name "")] + [decl-start (re-match-start match)] + [match-end (re-match-end match)] + [name-start (string-find-substring-from source name decl-start)] + [start (if (eq? kind 'statement-sequence) + decl-start + name-start)]) + (and start (cons start match-end)))) + +(def (scan-javascript-vardef-declarations rule path source spec) + (let* ([name (alist-ref/default spec 'name "")] + [value (alist-ref/default spec 'value "")] + [rx (re (string-append + "(var|let|const)[ \\t]+" + (regex-escape-string name) + "[ \\t]*=[ \\t]*" + (regex-escape-string value)))] + [len (string-length source)]) + (let loop ([start 0] [acc '()]) + (let ([match (re-search rx source start)]) + (if (not match) + (reverse acc) + (let* ([range + (and (identifier-boundary-before? + source + (re-match-start match)) + (identifier-boundary-after? + source + (re-match-end match)) + (javascript-vardef-declaration-range + source + spec + match))] + [finding + (and range + (javascript-vardef-assignment-finding + rule + path + source + (car range) + (cdr range)))] + [next (max (+ (re-match-start match) 1) + (re-match-end match))]) + (loop next (if finding (cons finding acc) acc)))))))) + +(def (scan-javascript-vardef-assign-pattern rule path source pattern) + (let ([option (rule-option-entry rule "vardef_assign")]) + (and option + (let ([spec (javascript-vardef-assign-pattern-spec pattern)]) + (and spec + (let ([assignments + (scan-javascript-vardef-actual-assignments + rule + path + source + spec)]) + (if (rule-option-explicit-false? rule "vardef_assign") + assignments + (append + (scan-javascript-vardef-declarations + rule + path + source + spec) + assignments)))))))) --- a/src/semgrep/scan.ss +++ b/src/semgrep/scan.ss @@ -14,6 +14,7 @@ (semgrep result extras) (semgrep result findings) (semgrep engine generic-scan) + (semgrep engine js-vardef-scan) (semgrep engine markup-scan) (semgrep engine regex-scan) (semgrep engine rule-plan) @@ -166,6 +167,24 @@ (substring text 0 (- len 2)) text))) +(def (identifier-boundary-before? source index) + (or (= index 0) + (not (identifier-char? (string-ref source (- index 1)))))) + +(def (identifier-boundary-after? source index) + (or (>= index (string-length source)) + (not (identifier-char? (string-ref source index))))) + +(def (previous-word-before source index) + (let ([before (skip-horizontal-backward source (- index 1))]) + (and (>= before 0) + (identifier-char? (string-ref source before)) + (let loop ([start before]) + (if (and (> start 0) + (identifier-char? (string-ref source (- start 1)))) + (loop (- start 1)) + (substring source start (+ before 1))))))) + (def (decorated-field-pattern-spec pattern) (let* ([trimmed (string-trim pattern)] [len (string-length trimmed)]) @@ -471,164 +490,6 @@ (loop next (if finding (cons finding acc) acc)))))))))) -(def (javascript-vardef-assign-pattern-kind pattern) - (let ([trimmed (string-trim pattern)]) - (cond - [(sg-string-prefix? "<..." trimmed) 'expression-ellipsis] - [(string-find-substring pattern "\n") 'statement-sequence] - [else 'plain]))) - -(def (javascript-vardef-assign-pattern-spec pattern) - (let* ([match (re-search - (re "([A-Za-z_$][A-Za-z0-9_$]*)[ \\t]*=[ \\t]*([^;>\\n]+)") - pattern - 0)] - [name (and match (re-match-group match 1))] - [value0 (and match (string-trim (re-match-group match 2)))] - [ellipsis (and value0 (string-find-substring value0 "..."))] - [value (and value0 - (if ellipsis - (string-trim (substring value0 0 ellipsis)) - value0))]) - (and name - value - (not (string-find-substring name "$")) - (not (string-find-substring value "$")) - (list (cons 'name name) - (cons 'value value) - (cons 'kind - (javascript-vardef-assign-pattern-kind pattern)))))) - -(def (identifier-boundary-before? source index) - (or (= index 0) - (not (identifier-char? (string-ref source (- index 1)))))) - -(def (identifier-boundary-after? source index) - (or (>= index (string-length source)) - (not (identifier-char? (string-ref source index))))) - -(def (previous-word-before source index) - (let ([before (skip-horizontal-backward source (- index 1))]) - (and (>= before 0) - (identifier-char? (string-ref source before)) - (let loop ([start before]) - (if (and (> start 0) - (identifier-char? (string-ref source (- start 1)))) - (loop (- start 1)) - (substring source start (+ before 1))))))) - -(def (javascript-declaration-keyword? word) - (or (string=? word "var") - (string=? word "let") - (string=? word "const"))) - -(def (javascript-vardef-assignment-preceded-by-declaration? source start) - (let ([word (previous-word-before source start)]) - (and word (javascript-declaration-keyword? word)))) - -(def (javascript-vardef-assignment-finding rule path source start end) - (finding-for-range-with-bindings rule path source start end '())) - -(def (scan-javascript-vardef-actual-assignments rule path source spec) - (let* ([name (alist-ref/default spec 'name "")] - [value (alist-ref/default spec 'value "")] - [rx (re (string-append - (regex-escape-string name) - "[ \\t]*=[ \\t]*" - (regex-escape-string value)))] - [len (string-length source)]) - (let loop ([start 0] [acc '()]) - (let ([match (re-search rx source start)]) - (if (not match) - (reverse acc) - (let* ([match-start (re-match-start match)] - [match-end (re-match-end match)] - [finding - (and (identifier-boundary-before? source match-start) - (identifier-boundary-after? - source - (+ match-start (string-length name))) - (identifier-boundary-after? source match-end) - (not (javascript-vardef-assignment-preceded-by-declaration? - source - match-start)) - (javascript-vardef-assignment-finding - rule - path - source - match-start - match-end))] - [next (max (+ match-start 1) match-end)]) - (loop next (if finding (cons finding acc) acc)))))))) - -(def (javascript-vardef-declaration-range source spec match) - (let* ([kind (alist-ref/default spec 'kind 'plain)] - [name (alist-ref/default spec 'name "")] - [decl-start (re-match-start match)] - [match-end (re-match-end match)] - [name-start (string-find-substring-from source name decl-start)] - [start (if (eq? kind 'statement-sequence) - decl-start - name-start)]) - (and start (cons start match-end)))) - -(def (scan-javascript-vardef-declarations rule path source spec) - (let* ([name (alist-ref/default spec 'name "")] - [value (alist-ref/default spec 'value "")] - [rx (re (string-append - "(var|let|const)[ \\t]+" - (regex-escape-string name) - "[ \\t]*=[ \\t]*" - (regex-escape-string value)))] - [len (string-length source)]) - (let loop ([start 0] [acc '()]) - (let ([match (re-search rx source start)]) - (if (not match) - (reverse acc) - (let* ([range - (and (identifier-boundary-before? - source - (re-match-start match)) - (identifier-boundary-after? - source - (re-match-end match)) - (javascript-vardef-declaration-range - source - spec - match))] - [finding - (and range - (javascript-vardef-assignment-finding - rule - path - source - (car range) - (cdr range)))] - [next (max (+ (re-match-start match) 1) - (re-match-end match))]) - (loop next (if finding (cons finding acc) acc)))))))) - -(def (scan-javascript-vardef-assign-pattern rule path source pattern) - (let ([option (rule-option-entry rule "vardef_assign")]) - (and option - (let ([spec (javascript-vardef-assign-pattern-spec pattern)]) - (and spec - (let ([assignments - (scan-javascript-vardef-actual-assignments - rule - path - source - spec)]) - (if (rule-option-explicit-false? rule "vardef_assign") - assignments - (append - (scan-javascript-vardef-declarations - rule - path - source - spec) - assignments)))))))) - (def (json-key-value-pattern-spec pattern) (let* ([match (re-search (re "\\$([A-Za-z_][A-Za-z0-9_]*)[ \\t]*:[ \\t]*([^,}\\n]+)")