Extract Semgrep Python return scanners

ober

60591bc6d82048b68e17c3c356d786551a179793

diff --git a/SEMGREP_JERBOA_IMPLEMENTATION.md b/SEMGREP_JERBOA_IMPLEMENTATION.md
index 4c40c8e..5f91542 100644
--- a/SEMGREP_JERBOA_IMPLEMENTATION.md
+++ b/SEMGREP_JERBOA_IMPLEMENTATION.md
@@ -503,6 +503,8 @@ Completed in the repo:
   - extracted Python f-string, interpolated call/assignment, PEP 614
     decorator, and f-string-equivalence fallback scanners into
     `src/semgrep/engine/py-fstring-scan.ss`
+  - extracted Python `return "..."` and `[...]` fallback scanners into
+    `src/semgrep/engine/py-return-scan.ss`
   - exported shared Python line-assignment analysis from
     `src/semgrep/engine/py-constant-prop.ss` so remaining Python fallbacks in
     `scan.ss` can reuse one assignment-info implementation
@@ -523,8 +525,9 @@ Remaining Phase 0 work:
 - Continue splitting `src/semgrep/scan.ss` into engine, targeting, text-mode,
   structural dispatch, taint, and result/output-adjacent modules. The Python
   constant-propagation and f-string/interpolated fallback surfaces are now
-  out; the next meaningful cuts are the remaining Python special-case scanners
-  plus the larger language dispatch and taint sections.
+  out, and the remaining Python return/list special cases are out too; the
+  next meaningful cuts are the Python import-local/equivalence special-case
+  scanners plus the larger language dispatch and taint sections.
 - Run broader upstream sweeps and grow the expected-fail baselines from real
   sampled data instead of one narrow seed run.
 
diff --git a/lib/semgrep/engine/py-return-scan.sls b/lib/semgrep/engine/py-return-scan.sls
new file mode 100644
index 0000000..e8a67e3
--- /dev/null
+++ b/lib/semgrep/engine/py-return-scan.sls
@@ -0,0 +1,210 @@
+#!chezscheme
+;;; Generated by jerbuild — DO NOT EDIT
+;;; Source: src/semgrep/engine/py-return-scan.ss
+
+(library (semgrep engine py-return-scan)
+  (export
+    scan-python-return-string-ellipsis-pattern
+    scan-python-list-ellipsis-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?)
+    (semgrep engine regex-support)
+    (semgrep engine text-support))
+  (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 (identifier-token-char? ch)
+       (or (char-alphabetic? ch)
+           (char-numeric? ch)
+           (char=? ch #\_)))
+  (def (token-start-boundary? source index)
+       (or (= index 0)
+           (not (identifier-token-char?
+                  (string-ref source (- index 1))))))
+  (def (token-end-boundary? source index)
+       (or (= index (string-length source))
+           (not (identifier-token-char? (string-ref source index)))))
+  (def (previous-nonspace-index source index)
+       (let loop ([i (- index 1)])
+         (cond
+           [(< i 0) #f]
+           [(char-whitespace? (string-ref source i)) (loop (- i 1))]
+           [else i])))
+  (def (find-matching-close-paren source open-index)
+       (let ([len (string-length source)])
+         (let loop ([i open-index]
+                    [depth 0]
+                    [state 'normal]
+                    [escaped? #f])
+           (cond
+             [(>= i len) #f]
+             [(eq? state 'normal)
+              (let ([ch (string-ref source i)])
+                (cond
+                  [(char=? ch #\") (loop (+ i 1) depth 'double #f)]
+                  [(char=? ch #\') (loop (+ i 1) depth 'single #f)]
+                  [(char=? ch #\`) (loop (+ i 1) depth 'backtick #f)]
+                  [(char=? ch #\() (loop (+ i 1) (+ depth 1) state #f)]
+                  [(char=? ch #\))
+                   (if (= depth 1)
+                       (+ i 1)
+                       (loop (+ i 1) (max 0 (- depth 1)) state #f))]
+                  [else (loop (+ i 1) depth state #f)]))]
+             [escaped? (loop (+ i 1) depth state #f)]
+             [(char=? (string-ref source i) #\\)
+              (loop (+ i 1) depth state #t)]
+             [(and (eq? state 'double)
+                   (char=? (string-ref source i) #\"))
+              (loop (+ i 1) depth 'normal #f)]
+             [(and (eq? state 'single)
+                   (char=? (string-ref source i) #\'))
+              (loop (+ i 1) depth 'normal #f)]
+             [(and (eq? state 'backtick)
+                   (char=? (string-ref source i) #\`))
+              (loop (+ i 1) depth 'normal #f)]
+             [else (loop (+ i 1) depth state #f)]))))
+  (def (find-matching-close-bracket source open-index)
+       (let ([len (string-length source)])
+         (let loop ([i open-index]
+                    [depth 0]
+                    [state 'normal]
+                    [escaped? #f])
+           (cond
+             [(>= i len) #f]
+             [(eq? state 'normal)
+              (let ([ch (string-ref source i)])
+                (cond
+                  [(char=? ch #\") (loop (+ i 1) depth 'double #f)]
+                  [(char=? ch #\') (loop (+ i 1) depth 'single #f)]
+                  [(char=? ch #\[) (loop (+ i 1) (+ depth 1) state #f)]
+                  [(char=? ch #\])
+                   (if (= depth 1)
+                       (+ i 1)
+                       (loop (+ i 1) (max 0 (- depth 1)) state #f))]
+                  [else (loop (+ i 1) depth state #f)]))]
+             [escaped? (loop (+ i 1) depth state #f)]
+             [(char=? (string-ref source i) #\\)
+              (loop (+ i 1) depth state #t)]
+             [(and (eq? state 'double)
+                   (char=? (string-ref source i) #\"))
+              (loop (+ i 1) depth 'normal #f)]
+             [(and (eq? state 'single)
+                   (char=? (string-ref source i) #\'))
+              (loop (+ i 1) depth 'normal #f)]
+             [else (loop (+ i 1) depth state #f)]))))
+  (def (python-simple-string-literal-prefix-char? ch)
+       (or (char=? ch #\r)
+           (char=? ch #\R)
+           (char=? ch #\u)
+           (char=? ch #\U)
+           (char=? ch #\b)
+           (char=? ch #\B)
+           (char=? ch #\f)
+           (char=? ch #\F)))
+  (def (python-string-literal-at? source i end)
+       (let loop ([j i])
+         (cond
+           [(>= j end) #f]
+           [(or (char=? (string-ref source j) #\")
+                (char=? (string-ref source j) #\'))
+            #t]
+           [(python-simple-string-literal-prefix-char?
+              (string-ref source j))
+            (loop (+ j 1))]
+           [else #f])))
+  (def (python-range-has-string-literal? source start end)
+       (let loop ([i start])
+         (and (< i end)
+              (or (and (token-start-boundary? source i)
+                       (python-string-literal-at? source i end))
+                  (loop (+ i 1))))))
+  (def (python-list-literal-open? source index)
+       (let ([prev (previous-nonspace-index source index)])
+         (or (not prev)
+             (let ([ch (string-ref source prev)])
+               (not (or (identifier-token-char? ch)
+                        (char=? ch #\])
+                        (char=? ch #\))
+                        (char=? ch #\")
+                        (char=? ch #\')))))))
+  (def (python-return-string-ellipsis-pattern? pattern)
+       (string=? (string-trim pattern) "return \"...\""))
+  (def (python-return-statement-end source first line-end)
+       (let ([expr-start (skip-horizontal-forward
+                           source
+                           (+ first (string-length "return")))])
+         (if (and (< expr-start (string-length source))
+                  (char=? (string-ref source expr-start) #\())
+             (or (find-matching-close-paren source expr-start) line-end)
+             line-end)))
+  (def (scan-python-return-string-ellipsis-pattern rule path source pattern initial-bindings)
+       (and (python-return-string-ellipsis-pattern? pattern)
+            (let ([len (string-length source)])
+              (let loop ([line-start 0] [acc '()])
+                (if (> line-start len)
+                    (nonempty-findings (reverse acc))
+                    (let* ([line-end (line-end-after source line-start)]
+                           [first (line-first-nonspace
+                                    source
+                                    line-start
+                                    line-end)]
+                           [line (substring source first line-end)]
+                           [statement-end (and (sg-string-prefix?
+                                                 "return"
+                                                 line)
+                                               (let ([after (+ first
+                                                               (string-length
+                                                                 "return"))])
+                                                 (and (token-end-boundary?
+                                                        source
+                                                        after)
+                                                      (python-return-statement-end
+                                                        source
+                                                        first
+                                                        line-end))))]
+                           [finding (and statement-end
+                                         (python-range-has-string-literal?
+                                           source
+                                           first
+                                           statement-end)
+                                         (finding-for-range-with-bindings rule path source first
+                                           statement-end
+                                           initial-bindings))]
+                           [next (if (< line-end len)
+                                     (+ line-end 1)
+                                     (+ len 1))])
+                      (loop next (if finding (cons finding acc) acc))))))))
+  (def (scan-python-list-ellipsis-pattern rule path source
+         pattern initial-bindings)
+       (and (string=? (string-trim pattern) "[...]")
+            (let ([len (string-length source)])
+              (let loop ([start 0] [acc '()])
+                (if (>= start len)
+                    (nonempty-findings (reverse acc))
+                    (let ([open (string-find-substring-from
+                                  source
+                                  "["
+                                  start)])
+                      (if (not open)
+                          (nonempty-findings (reverse acc))
+                          (let* ([close (and (python-list-literal-open?
+                                               source
+                                               open)
+                                             (find-matching-close-bracket
+                                               source
+                                               open))]
+                                 [finding (and close
+                                               (finding-for-range-with-bindings rule path source open
+                                                 close initial-bindings))]
+                                 [next (if close
+                                           (max (+ open 1) close)
+                                           (+ open 1))])
+                            (loop
+                              next
+                              (if finding (cons finding acc) acc)))))))))))
diff --git a/lib/semgrep/scan.sls b/lib/semgrep/scan.sls
index 3810adf..934913c 100644
--- a/lib/semgrep/scan.sls
+++ b/lib/semgrep/scan.sls
@@ -30,6 +30,7 @@
    (semgrep engine py-cp-scan)
    (semgrep engine py-constant-prop)
    (semgrep engine py-fstring-scan)
+   (semgrep engine py-return-scan)
    (semgrep engine py-string-eval)
    (semgrep engine py-string-scan) (semgrep engine regex-scan)
    (semgrep engine rule-plan) (semgrep engine regex-support)
@@ -8196,32 +8197,6 @@
                   (< start end))
              (finding-with-range finding source start end)
              finding)))
-  (def (python-simple-string-literal-prefix-char? ch)
-       (or (char=? ch #\r)
-           (char=? ch #\R)
-           (char=? ch #\u)
-           (char=? ch #\U)
-           (char=? ch #\b)
-           (char=? ch #\B)
-           (char=? ch #\f)
-           (char=? ch #\F)))
-  (def (python-string-literal-at? source i end)
-       (let loop ([j i])
-         (cond
-           [(>= j end) #f]
-           [(or (char=? (string-ref source j) #\")
-                (char=? (string-ref source j) #\'))
-            #t]
-           [(python-simple-string-literal-prefix-char?
-              (string-ref source j))
-            (loop (+ j 1))]
-           [else #f])))
-  (def (python-range-has-string-literal? source start end)
-       (let loop ([i start])
-         (and (< i end)
-              (or (and (token-start-boundary? source i)
-                       (python-string-literal-at? source i end))
-                  (loop (+ i 1))))))
   (def (find-matching-close-bracket source open-index)
        (let ([len (string-length source)])
          (let loop ([i open-index]
@@ -8251,90 +8226,6 @@
                    (char=? (string-ref source i) #\'))
               (loop (+ i 1) depth 'normal #f)]
              [else (loop (+ i 1) depth state #f)]))))
-  (def (python-list-literal-open? source index)
-       (let ([prev (previous-nonspace-index source index)])
-         (or (not prev)
-             (let ([ch (string-ref source prev)])
-               (not (or (identifier-token-char? ch)
-                        (char=? ch #\])
-                        (char=? ch #\))
-                        (char=? ch #\")
-                        (char=? ch #\')))))))
-  (def (python-return-string-ellipsis-pattern? pattern)
-       (string=? (string-trim pattern) "return \"...\""))
-  (def (python-return-statement-end source first line-end)
-       (let ([expr-start (skip-horizontal-forward
-                           source
-                           (+ first (string-length "return")))])
-         (if (and (< expr-start (string-length source))
-                  (char=? (string-ref source expr-start) #\())
-             (or (find-matching-close-paren source expr-start) line-end)
-             line-end)))
-  (def (scan-python-return-string-ellipsis-pattern rule path source pattern initial-bindings)
-       (and (python-return-string-ellipsis-pattern? pattern)
-            (let ([len (string-length source)])
-              (let loop ([line-start 0] [acc '()])
-                (if (> line-start len)
-                    (nonempty-findings (reverse acc))
-                    (let* ([line-end (line-end-after source line-start)]
-                           [first (line-first-nonspace
-                                    source
-                                    line-start
-                                    line-end)]
-                           [line (substring source first line-end)]
-                           [statement-end (and (sg-string-prefix?
-                                                 "return"
-                                                 line)
-                                               (let ([after (+ first
-                                                               (string-length
-                                                                 "return"))])
-                                                 (and (token-end-boundary?
-                                                        source
-                                                        after)
-                                                      (python-return-statement-end
-                                                        source
-                                                        first
-                                                        line-end))))]
-                           [finding (and statement-end
-                                         (python-range-has-string-literal?
-                                           source
-                                           first
-                                           statement-end)
-                                         (finding-for-range-with-bindings rule path source first
-                                           statement-end
-                                           initial-bindings))]
-                           [next (if (< line-end len)
-                                     (+ line-end 1)
-                                     (+ len 1))])
-                      (loop next (if finding (cons finding acc) acc))))))))
-  (def (scan-python-list-ellipsis-pattern rule path source
-         pattern initial-bindings)
-       (and (string=? (string-trim pattern) "[...]")
-            (let ([len (string-length source)])
-              (let loop ([start 0] [acc '()])
-                (if (>= start len)
-                    (nonempty-findings (reverse acc))
-                    (let ([open (string-find-substring-from
-                                  source
-                                  "["
-                                  start)])
-                      (if (not open)
-                          (nonempty-findings (reverse acc))
-                          (let* ([close (and (python-list-literal-open?
-                                               source
-                                               open)
-                                             (find-matching-close-bracket
-                                               source
-                                               open))]
-                                 [finding (and close
-                                               (finding-for-range-with-bindings rule path source open
-                                                 close initial-bindings))]
-                                 [next (if close
-                                           (max (+ open 1) close)
-                                           (+ open 1))])
-                            (loop
-                              next
-                              (if finding (cons finding acc) acc))))))))))
   (def (python-block-end
          source
          header-start
diff --git a/src/.jerbuild-hashes b/src/.jerbuild-hashes
index 3ff3157..e2a5d85 100644
--- a/src/.jerbuild-hashes
+++ b/src/.jerbuild-hashes
@@ -1,4 +1,7 @@
-(("src/semgrep/engine/py-string-scan.ss"
+(("src/semgrep/engine/py-return-scan.ss"
+   .
+   "B9F04A4640FFC26D")
+ ("src/semgrep/engine/py-string-scan.ss"
    .
    "DBAD99DB5C22F6A1")
  ("src/semgrep/util/literals.ss" . "8A094085551B216E")
@@ -15,10 +18,10 @@
  ("src/semgrep/engine/js-decorator-scan.ss"
    .
    "193758AD2E444FD6")
- ("src/semgrep/engine/ts-type-scan.ss" . "B36BAF07D19F4419")
- ("src/semgrep/output/text.ss" . "BE476CB84B807FBA")
- ("src/semgrep/rule.ss" . "E12C108153C181FA")
  ("src/semgrep/schema/lang.ss" . "CAE2CA859C9A9FD0")
+ ("src/semgrep/rule.ss" . "E12C108153C181FA")
+ ("src/semgrep/output/text.ss" . "BE476CB84B807FBA")
+ ("src/semgrep/engine/ts-type-scan.ss" . "B36BAF07D19F4419")
  ("src/semgrep/source/offsets.ss" . "834EFDB706823794")
  ("src/semgrep/engine/regex-support.ss" . "9FCF118903259C97")
  ("src/semgrep/engine/py-constant-scan.ss"
@@ -42,17 +45,17 @@
  ("src/semgrep/output/json.ss" . "293881CFA2ADB7BC")
  ("src/semgrep/lang.ss" . "6982E07679D20836")
  ("src/semgrep/parse/parse-target.ss" . "97AA8FFEB12736DA")
- ("src/semgrep/scan.ss" . "E17E372187C48088")
+ ("src/semgrep/scan.ss" . "D233411A6E763C9C")
  ("src/semgrep/engine/generic-scan.ss" . "F69D0ACD0DD62610")
- ("src/semgrep/engine/js-constructor-scan.ss"
-   .
-   "6B226B8D0584A7A")
  ("src/semgrep/engine/py-constant-prop.ss"
    .
    "76462D0EB71F2180")
+ ("src/semgrep/engine/js-constructor-scan.ss"
+   .
+   "6B226B8D0584A7A")
  ("src/semgrep/engine/comparison.ss" . "5B5731915BCB8E6")
  ("src/semgrep/main.ss" . "A4EC9E7F2A09D25E")
- ("src/semgrep/engine/text-support.ss" . "644AF29394C53045")
  ("src/semgrep/engine/ts-decorator-scan.ss"
    .
-   "610AA3A90D6A95F8"))
+   "610AA3A90D6A95F8")
+ ("src/semgrep/engine/text-support.ss" . "644AF29394C53045"))
diff --git a/src/semgrep/engine/py-return-scan.ss b/src/semgrep/engine/py-return-scan.ss
new file mode 100644
index 0000000..e913d37
--- /dev/null
+++ b/src/semgrep/engine/py-return-scan.ss
@@ -0,0 +1,208 @@
+(export
+  scan-python-return-string-ellipsis-pattern
+  scan-python-list-ellipsis-pattern)
+
+(import (except (jerboa prelude) meta atom?)
+        (semgrep engine regex-support)
+        (semgrep engine text-support))
+
+(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 (identifier-token-char? ch)
+  (or (char-alphabetic? ch)
+      (char-numeric? ch)
+      (char=? ch #\_)))
+
+(def (token-start-boundary? source index)
+  (or (= index 0)
+      (not (identifier-token-char? (string-ref source (- index 1))))))
+
+(def (token-end-boundary? source index)
+  (or (= index (string-length source))
+      (not (identifier-token-char? (string-ref source index)))))
+
+(def (previous-nonspace-index source index)
+  (let loop ([i (- index 1)])
+    (cond
+      [(< i 0) #f]
+      [(char-whitespace? (string-ref source i)) (loop (- i 1))]
+      [else i])))
+
+(def (find-matching-close-paren source open-index)
+  (let ([len (string-length source)])
+    (let loop ([i open-index] [depth 0] [state 'normal] [escaped? #f])
+      (cond
+        [(>= i len) #f]
+        [(eq? state 'normal)
+         (let ([ch (string-ref source i)])
+           (cond
+             [(char=? ch #\") (loop (+ i 1) depth 'double #f)]
+             [(char=? ch #\') (loop (+ i 1) depth 'single #f)]
+             [(char=? ch #\`) (loop (+ i 1) depth 'backtick #f)]
+             [(char=? ch #\() (loop (+ i 1) (+ depth 1) state #f)]
+             [(char=? ch #\))
+              (if (= depth 1)
+                  (+ i 1)
+                  (loop (+ i 1) (max 0 (- depth 1)) state #f))]
+             [else (loop (+ i 1) depth state #f)]))]
+        [escaped? (loop (+ i 1) depth state #f)]
+        [(char=? (string-ref source i) #\\)
+         (loop (+ i 1) depth state #t)]
+        [(and (eq? state 'double) (char=? (string-ref source i) #\"))
+         (loop (+ i 1) depth 'normal #f)]
+        [(and (eq? state 'single) (char=? (string-ref source i) #\'))
+         (loop (+ i 1) depth 'normal #f)]
+        [(and (eq? state 'backtick) (char=? (string-ref source i) #\`))
+         (loop (+ i 1) depth 'normal #f)]
+        [else (loop (+ i 1) depth state #f)]))))
+
+(def (find-matching-close-bracket source open-index)
+  (let ([len (string-length source)])
+    (let loop ([i open-index] [depth 0] [state 'normal] [escaped? #f])
+      (cond
+        [(>= i len) #f]
+        [(eq? state 'normal)
+         (let ([ch (string-ref source i)])
+           (cond
+             [(char=? ch #\") (loop (+ i 1) depth 'double #f)]
+             [(char=? ch #\') (loop (+ i 1) depth 'single #f)]
+             [(char=? ch #\[) (loop (+ i 1) (+ depth 1) state #f)]
+             [(char=? ch #\])
+              (if (= depth 1)
+                  (+ i 1)
+                  (loop (+ i 1) (max 0 (- depth 1)) state #f))]
+             [else (loop (+ i 1) depth state #f)]))]
+        [escaped? (loop (+ i 1) depth state #f)]
+        [(char=? (string-ref source i) #\\)
+         (loop (+ i 1) depth state #t)]
+        [(and (eq? state 'double) (char=? (string-ref source i) #\"))
+         (loop (+ i 1) depth 'normal #f)]
+        [(and (eq? state 'single) (char=? (string-ref source i) #\'))
+         (loop (+ i 1) depth 'normal #f)]
+        [else (loop (+ i 1) depth state #f)]))))
+
+(def (python-simple-string-literal-prefix-char? ch)
+  (or (char=? ch #\r)
+      (char=? ch #\R)
+      (char=? ch #\u)
+      (char=? ch #\U)
+      (char=? ch #\b)
+      (char=? ch #\B)
+      (char=? ch #\f)
+      (char=? ch #\F)))
+
+(def (python-string-literal-at? source i end)
+  (let loop ([j i])
+    (cond
+      [(>= j end) #f]
+      [(or (char=? (string-ref source j) #\")
+           (char=? (string-ref source j) #\'))
+       #t]
+      [(python-simple-string-literal-prefix-char? (string-ref source j))
+       (loop (+ j 1))]
+      [else #f])))
+
+(def (python-range-has-string-literal? source start end)
+  (let loop ([i start])
+    (and (< i end)
+         (or (and (token-start-boundary? source i)
+                  (python-string-literal-at? source i end))
+             (loop (+ i 1))))))
+
+(def (python-list-literal-open? source index)
+  (let ([prev (previous-nonspace-index source index)])
+    (or (not prev)
+        (let ([ch (string-ref source prev)])
+          (not (or (identifier-token-char? ch)
+                   (char=? ch #\])
+                   (char=? ch #\))
+                   (char=? ch #\")
+                   (char=? ch #\')))))))
+
+(def (python-return-string-ellipsis-pattern? pattern)
+  (string=? (string-trim pattern) "return \"...\""))
+
+(def (python-return-statement-end source first line-end)
+  (let ([expr-start (skip-horizontal-forward
+                      source
+                      (+ first (string-length "return")))])
+    (if (and (< expr-start (string-length source))
+             (char=? (string-ref source expr-start) #\())
+        (or (find-matching-close-paren source expr-start) line-end)
+        line-end)))
+
+(def (scan-python-return-string-ellipsis-pattern
+       rule
+       path
+       source
+       pattern
+       initial-bindings)
+  (and (python-return-string-ellipsis-pattern? pattern)
+       (let ([len (string-length source)])
+         (let loop ([line-start 0] [acc '()])
+           (if (> line-start len)
+               (nonempty-findings (reverse acc))
+               (let* ([line-end (line-end-after source line-start)]
+                      [first (line-first-nonspace source line-start line-end)]
+                      [line (substring source first line-end)]
+                      [statement-end
+                       (and (sg-string-prefix? "return" line)
+                            (let ([after (+ first (string-length "return"))])
+                              (and (token-end-boundary? source after)
+                                   (python-return-statement-end
+                                     source
+                                     first
+                                     line-end))))]
+                      [finding
+                       (and statement-end
+                            (python-range-has-string-literal?
+                              source
+                              first
+                              statement-end)
+                            (finding-for-range-with-bindings
+                              rule
+                              path
+                              source
+                              first
+                              statement-end
+                              initial-bindings))]
+                      [next (if (< line-end len) (+ line-end 1) (+ len 1))])
+                 (loop next
+                       (if finding (cons finding acc) acc))))))))
+
+(def (scan-python-list-ellipsis-pattern
+       rule
+       path
+       source
+       pattern
+       initial-bindings)
+  (and (string=? (string-trim pattern) "[...]")
+       (let ([len (string-length source)])
+         (let loop ([start 0] [acc '()])
+           (if (>= start len)
+               (nonempty-findings (reverse acc))
+               (let ([open (string-find-substring-from source "[" start)])
+                 (if (not open)
+                     (nonempty-findings (reverse acc))
+                     (let* ([close (and (python-list-literal-open? source open)
+                                        (find-matching-close-bracket
+                                          source
+                                          open))]
+                            [finding
+                             (and close
+                                  (finding-for-range-with-bindings
+                                    rule
+                                    path
+                                    source
+                                    open
+                                    close
+                                    initial-bindings))]
+                            [next (if close
+                                      (max (+ open 1) close)
+                                      (+ open 1))])
+                       (loop next
+                             (if finding (cons finding acc) acc))))))))))
diff --git a/src/semgrep/scan.ss b/src/semgrep/scan.ss
index 3337be1..de7ce73 100644
--- a/src/semgrep/scan.ss
+++ b/src/semgrep/scan.ss
@@ -29,6 +29,7 @@
         (semgrep engine py-cp-scan)
         (semgrep engine py-constant-prop)
         (semgrep engine py-fstring-scan)
+        (semgrep engine py-return-scan)
         (semgrep engine py-string-eval)
         (semgrep engine py-string-scan)
         (semgrep engine regex-scan)
@@ -7909,34 +7910,6 @@
         (finding-with-range finding source start end)
         finding)))
 
-(def (python-simple-string-literal-prefix-char? ch)
-  (or (char=? ch #\r)
-      (char=? ch #\R)
-      (char=? ch #\u)
-      (char=? ch #\U)
-      (char=? ch #\b)
-      (char=? ch #\B)
-      (char=? ch #\f)
-      (char=? ch #\F)))
-
-(def (python-string-literal-at? source i end)
-  (let loop ([j i])
-    (cond
-      [(>= j end) #f]
-      [(or (char=? (string-ref source j) #\")
-           (char=? (string-ref source j) #\'))
-       #t]
-      [(python-simple-string-literal-prefix-char? (string-ref source j))
-       (loop (+ j 1))]
-      [else #f])))
-
-(def (python-range-has-string-literal? source start end)
-  (let loop ([i start])
-    (and (< i end)
-         (or (and (token-start-boundary? source i)
-                  (python-string-literal-at? source i end))
-             (loop (+ i 1))))))
-
 (def (find-matching-close-bracket source open-index)
   (let ([len (string-length source)])
     (let loop ([i open-index] [depth 0] [state 'normal] [escaped? #f])
@@ -7962,100 +7935,6 @@
          (loop (+ i 1) depth 'normal #f)]
         [else (loop (+ i 1) depth state #f)]))))
 
-(def (python-list-literal-open? source index)
-  (let ([prev (previous-nonspace-index source index)])
-    (or (not prev)
-        (let ([ch (string-ref source prev)])
-          (not (or (identifier-token-char? ch)
-                   (char=? ch #\])
-                   (char=? ch #\))
-                   (char=? ch #\")
-                   (char=? ch #\')))))))
-
-(def (python-return-string-ellipsis-pattern? pattern)
-  (string=? (string-trim pattern) "return \"...\""))
-
-(def (python-return-statement-end source first line-end)
-  (let ([expr-start (skip-horizontal-forward
-                      source
-                      (+ first (string-length "return")))])
-    (if (and (< expr-start (string-length source))
-             (char=? (string-ref source expr-start) #\())
-        (or (find-matching-close-paren source expr-start) line-end)
-        line-end)))
-
-(def (scan-python-return-string-ellipsis-pattern
-       rule
-       path
-       source
-       pattern
-       initial-bindings)
-  (and (python-return-string-ellipsis-pattern? pattern)
-       (let ([len (string-length source)])
-         (let loop ([line-start 0] [acc '()])
-           (if (> line-start len)
-               (nonempty-findings (reverse acc))
-               (let* ([line-end (line-end-after source line-start)]
-                      [first (line-first-nonspace source line-start line-end)]
-                      [line (substring source first line-end)]
-                      [statement-end
-                       (and (sg-string-prefix? "return" line)
-                            (let ([after (+ first (string-length "return"))])
-                              (and (token-end-boundary? source after)
-                                   (python-return-statement-end
-                                     source
-                                     first
-                                     line-end))))]
-                      [finding
-                       (and statement-end
-                            (python-range-has-string-literal?
-                              source
-                              first
-                              statement-end)
-                            (finding-for-range-with-bindings
-                              rule
-                              path
-                              source
-                              first
-                              statement-end
-                              initial-bindings))]
-                      [next (if (< line-end len) (+ line-end 1) (+ len 1))])
-                 (loop next
-                       (if finding (cons finding acc) acc))))))))
-
-(def (scan-python-list-ellipsis-pattern
-       rule
-       path
-       source
-       pattern
-       initial-bindings)
-  (and (string=? (string-trim pattern) "[...]")
-       (let ([len (string-length source)])
-         (let loop ([start 0] [acc '()])
-           (if (>= start len)
-               (nonempty-findings (reverse acc))
-               (let ([open (string-find-substring-from source "[" start)])
-                 (if (not open)
-                     (nonempty-findings (reverse acc))
-                     (let* ([close (and (python-list-literal-open? source open)
-                                        (find-matching-close-bracket
-                                          source
-                                          open))]
-                            [finding
-                             (and close
-                                  (finding-for-range-with-bindings
-                                    rule
-                                    path
-                                    source
-                                    open
-                                    close
-                                    initial-bindings))]
-                            [next (if close
-                                      (max (+ open 1) close)
-                                      (+ open 1))])
-                       (loop next
-                             (if finding (cons finding acc) acc))))))))))
-
 (def (python-block-end source header-start include-if-chain?)
   (let* ([header-indent (line-indent-at-offset source header-start)]
          [header-end (line-end-after source header-start)]