Skip to content

Commit

Permalink
Disallow var syntax in string interpolation (#32948)
Browse files Browse the repository at this point in the history
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.

(cherry picked from commit 050160c)
  • Loading branch information
c42f authored and KristofferC committed Aug 25, 2019
1 parent e38bc7e commit f6e60ea
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 7 deletions.
9 changes: 8 additions & 1 deletion base/shell.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
isempty(st) && error("\$ right before end of command")
stpos, c = popfirst!(st)
isspace(c) && error("space not allowed right after \$")
ex, j = Meta.parse(s,stpos,greedy=false)
if startswith(SubString(s, stpos), "var\"")
# Disallow var"#" syntax in cmd interpolations.
# TODO: Allow only identifiers after the $ for consistency with
# string interpolation syntax (see #3150)
ex, j = :var, stpos+3
else
ex, j = Meta.parse(s,stpos,greedy=false)
end
last_parse = (stpos:prevind(s, j)) .+ s.offset
update_arg(ex);
s = SubString(s, j)
Expand Down
18 changes: 12 additions & 6 deletions src/julia-parser.scm
Original file line number Diff line number Diff line change
Expand Up @@ -2134,11 +2134,11 @@
(let* ((p (ts:port s))
(c (peek-char p)))
(cond ((identifier-start-char? c)
(let* ((atom (parse-atom s))
(let* ((t (require-token s))
(c (peek-char p)))
(if (ends-interpolated-atom? c)
atom
(error (string "interpolated variable $" atom " ends with invalid character \"" c "\"; use \"$(" atom ")\" instead.")))))
(take-token s)
(error (string "interpolated variable $" t " ends with invalid character \"" c "\"; use \"$(" t ")\" instead.")))))
((eqv? c #\()
(read-char p)
(let ((ex (parse-eq* s))
Expand Down Expand Up @@ -2285,14 +2285,20 @@
(if (closing-token? t)
(error (string "unexpected \"" (take-token s) "\"")))))
(take-token s)
(if (and (eq? t 'var) (eqv? (peek-token s) #\") (not (ts:space? s)))
(if (and (eq? t 'var)
(if (or (ts:pbtok s) (ts:last-tok s))
(and (eqv? (peek-token s) #\") (not (ts:space? s)))
;; Hack: avoid peek-token if possible to preserve
;; (io.pos (ts:port s)) for non-greedy Meta.parse
(eqv? (peek-char (ts:port s)) #\")))
(begin
;; var"funky identifier" syntax
(take-token s)
(peek-token s)
(take-token s) ;; leading "
(let ((str (parse-raw-literal s #\"))
(nxt (peek-token s)))
(if (and (symbol? nxt) (not (operator? nxt)) (not (ts:space? s)))
(error (string "suffix not allowed after `var\"" str "\"`")))
(error (string "suffix not allowed after `var\"" str "\"`")))
(symbol str)))
t))

Expand Down
9 changes: 9 additions & 0 deletions test/spawn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,15 @@ let c = setenv(`x`, "A"=>true)
@test_throws ArgumentError `"$c "`
end

# Interaction of cmd parsing with var syntax (#32408)
let var = "x", vars="z"
@test `ls $var` == Cmd(["ls", "x"])
@test `ls $vars` == Cmd(["ls", "z"])
@test `ls $var"y"` == Cmd(["ls", "xy"])
@test `ls "'$var'"` == Cmd(["ls", "'x'"])
@test `ls $var "y"` == Cmd(["ls", "x", "y"])
end

# equality tests for AndCmds
@test Base.AndCmds(`$echocmd abc`, `$echocmd def`) == Base.AndCmds(`$echocmd abc`, `$echocmd def`)
@test Base.AndCmds(`$echocmd abc`, `$echocmd def`) != Base.AndCmds(`$echocmd abc`, `$echocmd xyz`)
Expand Down
6 changes: 6 additions & 0 deletions test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1922,3 +1922,9 @@ end
@test Base.remove_linenums!(Meta.parse("try a catch var\"#\" b end")) ==
Expr(:try, Expr(:block, :a), Symbol("#"), Expr(:block, :b))
@test Meta.parse("(var\"function\" = 1,)") == Expr(:tuple, Expr(:(=), Symbol("function"), 1))
# Non-standard identifiers require parens for string interpolation
@test Meta.parse("\"\$var\\\"#\\\"\"") == Expr(:string, :var, "\"#\"")
@test Meta.parse("\"\$(var\"#\")\"") == Expr(:string, Symbol("#"))
# Stream positioning after parsing var
@test Meta.parse("var'", 1, greedy=false) == (:var, 4)

0 comments on commit f6e60ea

Please sign in to comment.