Skip to content

Commit 050160c

Browse files
c42fJeffBezanson
authored andcommitted
Disallow var syntax in string interpolation (#32948)
The var"##" syntax should be disabled in string interpolation. Disallow `var` syntax in command interpolations This is special cased for compatibility. A more general fix would be to make cmd interpolation syntax exactly the same as string interpolation.
1 parent d0b5d98 commit 050160c

File tree

4 files changed

+35
-7
lines changed

4 files changed

+35
-7
lines changed

base/shell.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,14 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
7070
isempty(st) && error("\$ right before end of command")
7171
stpos, c = popfirst!(st)
7272
isspace(c) && error("space not allowed right after \$")
73-
ex, j = Meta.parse(s,stpos,greedy=false)
73+
if startswith(SubString(s, stpos), "var\"")
74+
# Disallow var"#" syntax in cmd interpolations.
75+
# TODO: Allow only identifiers after the $ for consistency with
76+
# string interpolation syntax (see #3150)
77+
ex, j = :var, stpos+3
78+
else
79+
ex, j = Meta.parse(s,stpos,greedy=false)
80+
end
7481
last_parse = (stpos:prevind(s, j)) .+ s.offset
7582
update_arg(ex);
7683
s = SubString(s, j)

src/julia-parser.scm

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,11 +2134,11 @@
21342134
(let* ((p (ts:port s))
21352135
(c (peek-char p)))
21362136
(cond ((identifier-start-char? c)
2137-
(let* ((atom (parse-atom s))
2137+
(let* ((t (require-token s))
21382138
(c (peek-char p)))
21392139
(if (ends-interpolated-atom? c)
2140-
atom
2141-
(error (string "interpolated variable $" atom " ends with invalid character \"" c "\"; use \"$(" atom ")\" instead.")))))
2140+
(take-token s)
2141+
(error (string "interpolated variable $" t " ends with invalid character \"" c "\"; use \"$(" t ")\" instead.")))))
21422142
((eqv? c #\()
21432143
(read-char p)
21442144
(let ((ex (parse-eq* s))
@@ -2285,14 +2285,20 @@
22852285
(if (closing-token? t)
22862286
(error (string "unexpected \"" (take-token s) "\"")))))
22872287
(take-token s)
2288-
(if (and (eq? t 'var) (eqv? (peek-token s) #\") (not (ts:space? s)))
2288+
(if (and (eq? t 'var)
2289+
(if (or (ts:pbtok s) (ts:last-tok s))
2290+
(and (eqv? (peek-token s) #\") (not (ts:space? s)))
2291+
;; Hack: avoid peek-token if possible to preserve
2292+
;; (io.pos (ts:port s)) for non-greedy Meta.parse
2293+
(eqv? (peek-char (ts:port s)) #\")))
22892294
(begin
22902295
;; var"funky identifier" syntax
2291-
(take-token s)
2296+
(peek-token s)
2297+
(take-token s) ;; leading "
22922298
(let ((str (parse-raw-literal s #\"))
22932299
(nxt (peek-token s)))
22942300
(if (and (symbol? nxt) (not (operator? nxt)) (not (ts:space? s)))
2295-
(error (string "suffix not allowed after `var\"" str "\"`")))
2301+
(error (string "suffix not allowed after `var\"" str "\"`")))
22962302
(symbol str)))
22972303
t))
22982304

test/spawn.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,15 @@ let c = setenv(`x`, "A"=>true)
461461
@test_throws ArgumentError `"$c "`
462462
end
463463

464+
# Interaction of cmd parsing with var syntax (#32408)
465+
let var = "x", vars="z"
466+
@test `ls $var` == Cmd(["ls", "x"])
467+
@test `ls $vars` == Cmd(["ls", "z"])
468+
@test `ls $var"y"` == Cmd(["ls", "xy"])
469+
@test `ls "'$var'"` == Cmd(["ls", "'x'"])
470+
@test `ls $var "y"` == Cmd(["ls", "x", "y"])
471+
end
472+
464473
# equality tests for AndCmds
465474
@test Base.AndCmds(`$echocmd abc`, `$echocmd def`) == Base.AndCmds(`$echocmd abc`, `$echocmd def`)
466475
@test Base.AndCmds(`$echocmd abc`, `$echocmd def`) != Base.AndCmds(`$echocmd abc`, `$echocmd xyz`)

test/syntax.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,3 +1922,9 @@ end
19221922
@test Base.remove_linenums!(Meta.parse("try a catch var\"#\" b end")) ==
19231923
Expr(:try, Expr(:block, :a), Symbol("#"), Expr(:block, :b))
19241924
@test Meta.parse("(var\"function\" = 1,)") == Expr(:tuple, Expr(:(=), Symbol("function"), 1))
1925+
# Non-standard identifiers require parens for string interpolation
1926+
@test Meta.parse("\"\$var\\\"#\\\"\"") == Expr(:string, :var, "\"#\"")
1927+
@test Meta.parse("\"\$(var\"#\")\"") == Expr(:string, Symbol("#"))
1928+
# Stream positioning after parsing var
1929+
@test Meta.parse("var'", 1, greedy=false) == (:var, 4)
1930+

0 commit comments

Comments
 (0)