Structural matcher: semgrep typed-metavariable syntax `(Type $VAR)` for Java
ober
8240b87351fbcc0fca46383a62c84de82975d372
--- a/lib/semgrep/match/structural.sls +++ b/lib/semgrep/match/structural.sls @@ -112,6 +112,60 @@ (loop (+ i 1) (cons ch acc)))) (loop (+ i 1) (cons ch acc)))) (loop (+ i 1) (cons ch acc)))))))) + (def (java-type-char? c) + (or (and (char<=? #\a c) (char<=? c #\z)) + (and (char<=? #\A c) (char<=? c #\Z)) + (and (char<=? #\0 c) (char<=? c #\9)) + (memv c '(#\_ #\. #\< #\> #\, #\[ #\])))) + (def (java-typed-metavar-content source start len) + (let ([s1 (go-skip-spaces source start len)]) + (and (< s1 len) + (let ([c0 (string-ref source s1)]) + (or (and (char<=? #\a c0) (char<=? c0 #\z)) + (and (char<=? #\A c0) (char<=? c0 #\Z)) + (char=? c0 #\_))) + (let scan-type ([k s1]) + (if (and (< k len) (java-type-char? (string-ref source k))) + (scan-type (+ k 1)) + (let ([sp (go-skip-spaces source k len)]) + (and (> sp k) + (< sp len) + (char=? (string-ref source sp) #\$) + (< (+ sp 1) len) + (identifier-start? (string-ref source (+ sp 1))) + (go-scan-ident source (+ sp 1) len)))))))) + (def (java-strip-typed-metavars source) + (let ([len (string-length source)]) + (let loop ([i 0] [acc '()]) + (if (>= i len) + (list->string (reverse acc)) + (let ([ch (string-ref source i)]) + (if (char=? ch #\() + (let* ([content-end (java-typed-metavar-content + source + (+ i 1) + len)] + [close (and content-end + (go-skip-spaces + source + content-end + len))]) + (if (and close + (< close len) + (char=? (string-ref source close) #\))) + (loop + (+ close 1) + (append + (reverse + (string->list + (string-trim + (substring + source + (+ i 1) + content-end)))) + acc)) + (loop (+ i 1) (cons ch acc)))) + (loop (+ i 1) (cons ch acc)))))))) (def (sg-string-prefix? prefix s) (let ([prefix-len (string-length prefix)] [len (string-length s)]) @@ -229,6 +283,9 @@ (def (rewrite-metavariables language source) (let* ([go? (go-language? language)] [source (if go? (go-strip-typed-metavars source) source)] + [source (if (string=? language "java") + (java-strip-typed-metavars source) + source)] [len (string-length source)]) (let loop ([i 0] [state 'normal] --- a/src/semgrep/match/structural.ss +++ b/src/semgrep/match/structural.ss @@ -128,6 +128,46 @@ (loop (+ i 1) (cons ch acc)))) (loop (+ i 1) (cons ch acc)))))))) +(def (java-type-char? c) + (or (and (char<=? #\a c) (char<=? c #\z)) + (and (char<=? #\A c) (char<=? c #\Z)) + (and (char<=? #\0 c) (char<=? c #\9)) + (memv c '(#\_ #\. #\< #\> #\, #\[ #\])))) + +(def (java-typed-metavar-content source start len) + (let ([s1 (go-skip-spaces source start len)]) + (and (< s1 len) + (let ([c0 (string-ref source s1)]) + (or (and (char<=? #\a c0) (char<=? c0 #\z)) + (and (char<=? #\A c0) (char<=? c0 #\Z)) + (char=? c0 #\_))) + (let scan-type ([k s1]) + (if (and (< k len) (java-type-char? (string-ref source k))) + (scan-type (+ k 1)) + (let ([sp (go-skip-spaces source k len)]) + (and (> sp k) (< sp len) + (char=? (string-ref source sp) #\$) + (< (+ sp 1) len) + (identifier-start? (string-ref source (+ sp 1))) + (go-scan-ident source (+ sp 1) len)))))))) + +(def (java-strip-typed-metavars source) + (let ([len (string-length source)]) + (let loop ([i 0] [acc '()]) + (if (>= i len) + (list->string (reverse acc)) + (let ([ch (string-ref source i)]) + (if (char=? ch #\() + (let* ([content-end (java-typed-metavar-content source (+ i 1) len)] + [close (and content-end (go-skip-spaces source content-end len))]) + (if (and close (< close len) (char=? (string-ref source close) #\))) + (loop (+ close 1) + (append (reverse (string->list + (string-trim (substring source (+ i 1) content-end)))) + acc)) + (loop (+ i 1) (cons ch acc)))) + (loop (+ i 1) (cons ch acc)))))))) + (def (sg-string-prefix? prefix s) (let ([prefix-len (string-length prefix)] [len (string-length s)]) @@ -266,6 +306,9 @@ (def (rewrite-metavariables language source) (let* ([go? (go-language? language)] [source (if go? (go-strip-typed-metavars source) source)] + [source (if (string=? language "java") + (java-strip-typed-metavars source) + source)] [len (string-length source)]) (let loop ([i 0] [state 'normal] [escaped? #f] [paren-depth 0] [brace-depth 0] [acc '()])