diff --git a/Makefile b/Makefile index eb6e54ae70b34..046f18492bc3e 100644 --- a/Makefile +++ b/Makefile @@ -365,10 +365,6 @@ endif # Remove various files which should not be installed -rm -f $(DESTDIR)$(datarootdir)/julia/base/version_git.sh -rm -f $(DESTDIR)$(datarootdir)/julia/test/Makefile - -rm -f $(DESTDIR)$(datarootdir)/julia/base/*/source-extracted - -rm -f $(DESTDIR)$(datarootdir)/julia/base/*/build-configured - -rm -f $(DESTDIR)$(datarootdir)/julia/base/*/build-compiled - -rm -f $(DESTDIR)$(datarootdir)/julia/base/*/build-checked -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/source-extracted -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/build-configured -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/build-compiled diff --git a/NEWS.md b/NEWS.md index d73373d95d26e..6c60b56b7a028 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,9 +4,6 @@ Julia v1.10 Release Notes New language features --------------------- -* JuliaSyntax.jl is now used as the default parser, providing better diagnostics and faster - parsing. Set environment variable `JULIA_USE_NEW_PARSER` to `0` to switch back to the old - parser if necessary (and if you find this necessary, please file an issue) ([#46372]). * `⥺` (U+297A, `\leftarrowsubset`) and `⥷` (U+2977, `\leftarrowless`) may now be used as binary operators with arrow precedence. ([#45962]) diff --git a/base/.gitignore b/base/.gitignore index 0fab5b41fda08..e572b8ea229d0 100644 --- a/base/.gitignore +++ b/base/.gitignore @@ -8,4 +8,3 @@ /version_git.jl /version_git.jl.phony /userimg.jl -/JuliaSyntax diff --git a/base/Base.jl b/base/Base.jl index 1fc20293aa384..1a677bf508977 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -489,10 +489,6 @@ a_method_to_overwrite_in_test() = inferencebarrier(1) include(mod::Module, _path::AbstractString) = _include(identity, mod, _path) include(mapexpr::Function, mod::Module, _path::AbstractString) = _include(mapexpr, mod, _path) -# External libraries vendored into Base -Core.println("JuliaSyntax/src/JuliaSyntax.jl") -include(@__MODULE__, "JuliaSyntax/src/JuliaSyntax.jl") - end_base_include = time_ns() const _sysimage_modules = PkgId[] @@ -604,9 +600,6 @@ function __init__() _require_world_age[] = get_world_counter() # Prevent spawned Julia process from getting stuck waiting on Tracy to connect. delete!(ENV, "JULIA_WAIT_FOR_TRACY") - if get_bool_env("JULIA_USE_NEW_PARSER", true) === true - JuliaSyntax.enable_in_core!() - end nothing end diff --git a/base/boot.jl b/base/boot.jl index 6698d4360cc7d..ec25fa2bc0b6d 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -825,11 +825,9 @@ Integer(x::Union{Float16, Float32, Float64}) = Int(x) # `_parse` must return an `svec` containing an `Expr` and the new offset as an # `Int`. # -# The internal jl_parse will call into Core._parse if not `nothing`. +# The internal jl_parse which will call into Core._parse if not `nothing`. _parse = nothing -_setparser!(parser) = setglobal!(Core, :_parse, parser) - # support for deprecated uses of internal _apply function _apply(x...) = Core._apply_iterate(Main.Base.iterate, x...) diff --git a/base/client.jl b/base/client.jl index 6e30c9991e45e..dd529dad5281e 100644 --- a/base/client.jl +++ b/base/client.jl @@ -202,7 +202,10 @@ parse_input_line(s::AbstractString) = parse_input_line(String(s)) # detect the reason which caused an :incomplete expression # from the error message # NOTE: the error messages are defined in src/julia-parser.scm -function fl_incomplete_tag(msg::AbstractString) +incomplete_tag(ex) = :none +function incomplete_tag(ex::Expr) + Meta.isexpr(ex, :incomplete) || return :none + msg = ex.args[1] occursin("string", msg) && return :string occursin("comment", msg) && return :comment occursin("requires end", msg) && return :block @@ -211,20 +214,6 @@ function fl_incomplete_tag(msg::AbstractString) return :other end -incomplete_tag(ex) = :none -function incomplete_tag(ex::Expr) - if ex.head !== :incomplete - return :none - elseif isempty(ex.args) - return :other - elseif ex.args[1] isa String - return fl_incomplete_tag(ex.args[1]) - else - return incomplete_tag(ex.args[1]) - end -end -incomplete_tag(exc::Meta.ParseError) = incomplete_tag(exc.detail) - function exec_options(opts) quiet = (opts.quiet != 0) startup = (opts.startupfile != 2) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 04b0791d9a79e..58f77078ddb5e 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -171,7 +171,7 @@ include("compiler/bootstrap.jl") ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_toplevel) include("compiler/parsing.jl") -Core._setparser!(fl_parse) +Core.eval(Core, :(_parse = Compiler.fl_parse)) end # baremodule Compiler )) diff --git a/base/errorshow.jl b/base/errorshow.jl index 81f4c9c2ee9e0..24bd3a37d5298 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -35,13 +35,6 @@ show_index(io::IO, x::LogicalIndex) = summary(io, x.mask) show_index(io::IO, x::OneTo) = print(io, "1:", x.stop) show_index(io::IO, x::Colon) = print(io, ':') -function showerror(io::IO, ex::Meta.ParseError) - if isnothing(ex.detail) - print(io, "ParseError(", repr(ex.msg), ")") - else - showerror(io, ex.detail) - end -end function showerror(io::IO, ex::BoundsError) print(io, "BoundsError") diff --git a/base/meta.jl b/base/meta.jl index ba2a5eeb6858b..b0e0dc371b26c 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -187,11 +187,8 @@ expression. """ struct ParseError <: Exception msg::String - detail::Any end -ParseError(msg::AbstractString) = ParseError(msg, nothing) - function _parse_string(text::AbstractString, filename::AbstractString, lineno::Integer, index::Integer, options) if index < 1 || index > ncodeunits(text) + 1 @@ -236,11 +233,7 @@ function parse(str::AbstractString, pos::Integer; greedy::Bool=true, raise::Bool depwarn::Bool=true) ex, pos = _parse_string(str, "none", 1, pos, greedy ? :statement : :atom) if raise && isa(ex,Expr) && ex.head === :error - err = ex.args[1] - if err isa String - err = ParseError(err) # For flisp parser - end - throw(err) + throw(ParseError(ex.args[1])) end return ex, pos end @@ -254,22 +247,20 @@ syntax errors will raise an error; otherwise, `parse` will return an expression raise an error upon evaluation. If `depwarn` is `false`, deprecation warnings will be suppressed. -```jldoctest; filter=r"(?<=Expr\\(:error).*|(?<=Expr\\(:incomplete).*" +```jldoctest julia> Meta.parse("x = 3") :(x = 3) +julia> Meta.parse("x = ") +:($(Expr(:incomplete, "incomplete: premature end of input"))) + julia> Meta.parse("1.0.2") -ERROR: ParseError: -# Error @ none:1:1 -1.0.2 -└──┘ ── invalid numeric constant +ERROR: Base.Meta.ParseError("invalid numeric constant \\\"1.0.\\\"") +Stacktrace: [...] julia> Meta.parse("1.0.2"; raise = false) -:(\$(Expr(:error, "invalid numeric constant \"1.0.\""))) - -julia> Meta.parse("x = ") -:(\$(Expr(:incomplete, "incomplete: premature end of input"))) +:($(Expr(:error, "invalid numeric constant \"1.0.\""))) ``` """ function parse(str::AbstractString; raise::Bool=true, depwarn::Bool=true) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 7312726fe2eaa..8fa40e4920eea 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -153,6 +153,7 @@ if Artifacts !== nothing """ end + Pkg = get(Base.loaded_modules, Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg"), nothing) diff --git a/deps/JuliaSyntax.mk b/deps/JuliaSyntax.mk deleted file mode 100644 index e9cc0c942dbe0..0000000000000 --- a/deps/JuliaSyntax.mk +++ /dev/null @@ -1,16 +0,0 @@ -$(eval $(call git-external,JuliaSyntax,JULIASYNTAX,,,$(BUILDDIR))) - -$(BUILDDIR)/$(JULIASYNTAX_SRC_DIR)/build-compiled: $(BUILDDIR)/$(JULIASYNTAX_SRC_DIR)/source-extracted - @# no build steps - echo 1 > $@ - -$(eval $(call symlink_install,JuliaSyntax,$$(JULIASYNTAX_SRC_DIR),$$(JULIAHOME)/base)) - -clean-JuliaSyntax: - -rm -f $(BUILDDIR)/$(JULIASYNTAX_SRC_DIR)/build-compiled -get-JuliaSyntax: $(JULIASYNTAX_SRC_FILE) -extract-JuliaSyntax: $(BUILDDIR)/$(JULIASYNTAX_SRC_DIR)/source-extracted -configure-JuliaSyntax: extract-JuliaSyntax -compile-JuliaSyntax: $(BUILDDIR)/$(JULIASYNTAX_SRC_DIR)/build-compiled -fastcheck-JuliSyntax: check-JuliSyntax -check-JuliSyntax: compile-JuliSyntax diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version deleted file mode 100644 index 2bd765e6f4535..0000000000000 --- a/deps/JuliaSyntax.version +++ /dev/null @@ -1,4 +0,0 @@ -JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = ec51994833d78f8c5525bc1647f448dfadc370c1 -JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git -JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1 diff --git a/deps/Makefile b/deps/Makefile index ac899b634a3fa..62bb85e72c492 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -36,7 +36,7 @@ BUILDDIR := $(BUILDDIR)$(MAYBE_HOST) # prevent installing libs into usr/lib64 on opensuse unexport CONFIG_SITE -DEP_LIBS := JuliaSyntax +DEP_LIBS := ifeq ($(USE_SYSTEM_LIBBLASTRAMPOLINE), 0) DEP_LIBS += blastrampoline @@ -188,7 +188,7 @@ DEP_LIBS_STAGED := $(DEP_LIBS) DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ openlibm dsfmt blastrampoline openblas lapack gmp mpfr patchelf utf8proc \ objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib p7zip csl \ - libsuitesparse lld libtracyclient ittapi JuliaSyntax + libsuitesparse lld libtracyclient ittapi DEP_LIBS_ALL := $(DEP_LIBS_STAGED_ALL) ifneq ($(USE_BINARYBUILDER_OPENBLAS),0) @@ -248,7 +248,4 @@ include $(SRCDIR)/libwhich.mk include $(SRCDIR)/p7zip.mk include $(SRCDIR)/libtracyclient.mk -# vendored Julia libs -include $(SRCDIR)/JuliaSyntax.mk - include $(SRCDIR)/tools/uninstallers.mk diff --git a/deps/checksums/JuliaSyntax-ec51994833d78f8c5525bc1647f448dfadc370c1.tar.gz/md5 b/deps/checksums/JuliaSyntax-ec51994833d78f8c5525bc1647f448dfadc370c1.tar.gz/md5 deleted file mode 100644 index e1f51dd3d711a..0000000000000 --- a/deps/checksums/JuliaSyntax-ec51994833d78f8c5525bc1647f448dfadc370c1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b1d1ccb00e422eb8b70b2120d7083bf3 diff --git a/deps/checksums/JuliaSyntax-ec51994833d78f8c5525bc1647f448dfadc370c1.tar.gz/sha512 b/deps/checksums/JuliaSyntax-ec51994833d78f8c5525bc1647f448dfadc370c1.tar.gz/sha512 deleted file mode 100644 index 2ac2b9ed7c903..0000000000000 --- a/deps/checksums/JuliaSyntax-ec51994833d78f8c5525bc1647f448dfadc370c1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e6df6dc2b5d2a5618da0d553eed793e1192147175d84d51f725c0ea8f7b6be92fbeb37de9abee2b2f548b0f0736f836ec7e3e20e93c12f77e1a2b2058bbfd6db diff --git a/src/ast.c b/src/ast.c index 06727b453d6a3..bd1ffee5b76b1 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1348,8 +1348,8 @@ jl_value_t *jl_parse(const char *text, size_t text_len, jl_value_t *filename, jl_svecset(args[1], 0, jl_box_uint8pointer((uint8_t*)text)); jl_svecset(args[1], 1, jl_box_long(text_len)); args[2] = filename; - args[3] = jl_box_long(lineno); - args[4] = jl_box_long(offset); + args[3] = jl_box_ulong(lineno); + args[4] = jl_box_ulong(offset); args[5] = options; jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; diff --git a/src/toplevel.c b/src/toplevel.c index 51ff93488426f..cf0104879a7b0 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -656,28 +656,19 @@ static void check_macro_rename(jl_sym_t *from, jl_sym_t *to, const char *keyword jl_errorf("cannot rename non-macro \"%s\" to macro \"%s\" in \"%s\"", n1, n2, keyword); } -// Eval `throw(ErrorException(msg)))` in module `m`. -// Used in `jl_toplevel_eval_flex` instead of `jl_throw` so that the error +// Format msg and eval `throw(ErrorException(msg)))` in module `m`. +// Used in `jl_toplevel_eval_flex` instead of `jl_errorf` so that the error // location in julia code gets into the backtrace. -static void jl_eval_throw(jl_module_t *m, jl_value_t *exc) +static void jl_eval_errorf(jl_module_t *m, const char* fmt, ...) { jl_value_t *throw_ex = (jl_value_t*)jl_exprn(jl_call_sym, 2); JL_GC_PUSH1(&throw_ex); jl_exprargset(throw_ex, 0, jl_builtin_throw); - jl_exprargset(throw_ex, 1, exc); - jl_toplevel_eval_flex(m, throw_ex, 0, 0); - JL_GC_POP(); -} - -// Format error message and call jl_eval -static void jl_eval_errorf(jl_module_t *m, const char* fmt, ...) -{ va_list args; va_start(args, fmt); - jl_value_t *exc = jl_vexceptionf(jl_errorexception_type, fmt, args); + jl_exprargset(throw_ex, 1, jl_vexceptionf(jl_errorexception_type, fmt, args)); va_end(args); - JL_GC_PUSH1(&exc); - jl_eval_throw(m, exc); + jl_toplevel_eval_flex(m, throw_ex, 0, 0); JL_GC_POP(); } @@ -884,7 +875,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int jl_eval_errorf(m, "malformed \"%s\" expression", jl_symbol_name(head)); if (jl_is_string(jl_exprarg(ex, 0))) jl_eval_errorf(m, "syntax: %s", jl_string_data(jl_exprarg(ex, 0))); - jl_eval_throw(m, jl_exprarg(ex, 0)); + jl_throw(jl_exprarg(ex, 0)); } else if (jl_is_symbol(ex)) { JL_GC_POP(); diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 20d26953eb22b..6ec7074f105fd 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -232,10 +232,7 @@ function complete_keyword(s::Union{String,SubString{String}}) Completion[KeywordCompletion(kw) for kw in sorted_keywords[r]] end -function complete_path(path::AbstractString, pos::Int; - use_envpath=false, shell_escape=false, - string_escape=false) - @assert !(shell_escape && string_escape) +function complete_path(path::AbstractString, pos::Int; use_envpath=false, shell_escape=false) if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) # if the path is just "~", don't consider the expanded username as a prefix if path == "~" @@ -262,9 +259,9 @@ function complete_path(path::AbstractString, pos::Int; matches = Set{String}() for file in files if startswith(file, prefix) - p = joinpath(dir, file) - is_dir = try isdir(p) catch; false end - push!(matches, is_dir ? joinpath(file, "") : file) + id = try isdir(joinpath(dir, file)) catch; false end + # joinpath is not used because windows needs to complete with double-backslash + push!(matches, id ? file * (@static Sys.iswindows() ? "\\\\" : "/") : file) end end @@ -310,14 +307,8 @@ function complete_path(path::AbstractString, pos::Int; end end - function do_escape(s) - return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") : - string_escape ? escape_string(s, ('\"','$')) : - s - end - - matchList = Completion[PathCompletion(do_escape(s)) for s in matches] - startpos = pos - lastindex(do_escape(prefix)) + 1 + matchList = Completion[PathCompletion(shell_escape ? replace(s, r"\s" => s"\\\0") : s) for s in matches] + startpos = pos - lastindex(prefix) + 1 - count(isequal(' '), prefix) # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`, # hence we need to add one to get the first index. This is also correct when considering # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`. @@ -776,7 +767,7 @@ end function close_path_completion(str, startpos, r, paths, pos) length(paths) == 1 || return false # Only close if there's a single choice... _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path - path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\""))) + path = expanduser(replace(_path, r"\\ " => " ")) # ...except if it's a directory... try isdir(path) @@ -1048,44 +1039,23 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0) return complete_identifiers!(Completion[], ffunc, context_module, string, string[startpos:pos], pos, dotpos, startpos) - elseif inc_tag === :cmd + # otherwise... + elseif inc_tag in [:cmd, :string] m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) startpos = nextind(partial, reverseind(partial, m.offset)) r = startpos:pos - # This expansion with "\\ "=>' ' replacement and shell_escape=true - # assumes the path isn't further quoted within the cmd backticks. expanded = complete_expanduser(replace(string[r], r"\\ " => " "), r) expanded[3] && return expanded # If user expansion available, return it - paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos, - shell_escape=true) - - return sort!(paths, by=p->p.path), r, success - elseif inc_tag === :string - # Find first non-escaped quote - m = match(r"\"(?!\\)", reverse(partial)) - startpos = nextind(partial, reverseind(partial, m.offset)) - r = startpos:pos - - expanded = complete_expanduser(string[r], r) - expanded[3] && return expanded # If user expansion available, return it + paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos) - path_prefix = try - unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\"")) - catch - nothing + if inc_tag === :string && close_path_completion(string, startpos, r, paths, pos) + paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") end - if !isnothing(path_prefix) - paths, r, success = complete_path(path_prefix, pos, string_escape=true) - if close_path_completion(string, startpos, r, paths, pos) - paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") - end - - # Fallthrough allowed so that Latex symbols can be completed in strings - success && return sort!(paths, by=p->p.path), r, success - end + #Latex symbols can be completed for strings + (success || inc_tag === :cmd) && return sort!(paths, by=p->p.path), r, success end ok, ret = bslash_completions(string, pos) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index b2199e10bef55..b0d1ff4b5237a 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1177,7 +1177,7 @@ let current_dir, forbidden catch e e isa Base.IOError && occursin("ELOOP", e.msg) end - c, r = test_complete("\""*escape_string(joinpath(path, "selfsym"))) + c, r = test_complete("\"$(joinpath(path, "selfsym"))") @test c == ["selfsymlink"] end end @@ -1207,62 +1207,26 @@ end mktempdir() do path space_folder = randstring() * " α" dir = joinpath(path, space_folder) + dir_space = replace(space_folder, " " => "\\ ") + mkdir(dir) cd(path) do - touch(joinpath(space_folder, "space .file")) - - dir_space = replace(space_folder, " " => "\\ ") - s = Sys.iswindows() ? "cd $dir_space\\\\space" : "cd $dir_space/space" - c, r = test_scomplete(s) - @test s[r] == "space" - @test "space\\ .file" in c - # Also use shell escape rules within cmd backticks - s = "`$s" - c, r = test_scomplete(s) - @test s[r] == "space" - @test "space\\ .file" in c - - # escape string according to Julia escaping rules - julia_esc(str) = escape_string(str, ('\"','$')) - - # For normal strings the string should be properly escaped according to - # the usual rules for Julia strings. - s = "cd(\"" * julia_esc(joinpath(path, space_folder, "space")) - c, r = test_complete(s) - @test s[r] == "space" - @test "space .file\"" in c - - # '$' is the only character which can appear in a windows filename and - # which needs to be escaped in Julia strings (on unix we could do this - # test with all sorts of special chars) - touch(joinpath(space_folder, "needs_escape\$.file")) - escpath = julia_esc(joinpath(path, space_folder, "needs_escape\$")) - s = "cd(\"$escpath" - c, r = test_complete(s) - @test s[r] == "needs_escape\\\$" - @test "needs_escape\\\$.file\"" in c + open(joinpath(space_folder, "space .file"),"w") do f + s = Sys.iswindows() ? "rm $dir_space\\\\space" : "cd $dir_space/space" + c, r = test_scomplete(s) + @test r == lastindex(s)-4:lastindex(s) + @test "space\\ .file" in c - if !Sys.iswindows() - touch(joinpath(space_folder, "needs_escape2\n\".file")) - escpath = julia_esc(joinpath(path, space_folder, "needs_escape2\n\"")) - s = "cd(\"$escpath" + s = Sys.iswindows() ? "cd(\"β $dir_space\\\\space" : "cd(\"β $dir_space/space" c, r = test_complete(s) - @test s[r] == "needs_escape2\\n\\\"" - @test "needs_escape2\\n\\\".file\"" in c - - touch(joinpath(space_folder, "needs_escape3\\.file")) - escpath = julia_esc(joinpath(path, space_folder, "needs_escape3\\")) - s = "cd(\"$escpath" - c, r = test_complete(s) - @test s[r] == "needs_escape3\\\\" - @test "needs_escape3\\\\.file\"" in c + @test r == lastindex(s)-4:lastindex(s) + @test "space .file\"" in c end - # Test for issue #10324 - s = "cd(\"$space_folder" + s = "cd(\"$dir_space" c, r = test_complete(s) - @test r == 5:14 - @test s[r] == space_folder + @test r == 5:15 + @test s[r] == dir_space #Test for #18479 for c in "'`@\$;&" @@ -1276,9 +1240,8 @@ mktempdir() do path @test c[1] == test_dir*(Sys.iswindows() ? "\\\\" : "/") @test res end - escdir = julia_esc(test_dir) - c, r, res = test_complete("\""*escdir) - @test c[1] == escdir*(Sys.iswindows() ? "\\\\" : "/") + c, r, res = test_complete("\""*test_dir) + @test c[1] == test_dir*(Sys.iswindows() ? "\\\\" : "/") @test res finally rm(joinpath(path, test_dir), recursive=true) @@ -1322,7 +1285,7 @@ if Sys.iswindows() @test r == length(s)-1:length(s) @test file in c - s = "cd(\"..\\\\" + s = "cd(\"..\\" c,r = test_complete(s) @test r == length(s)+1:length(s) @test temp_name * "\\\\" in c diff --git a/test/backtrace.jl b/test/backtrace.jl index 50a50100488c4..38019880da35d 100644 --- a/test/backtrace.jl +++ b/test/backtrace.jl @@ -195,13 +195,6 @@ let bt, found = false end # Syntax error locations appear in backtraces -let trace = try - eval(Expr(:error, 1)) - catch - stacktrace(catch_backtrace()) - end - @test trace[1].func === Symbol("top-level scope") -end let trace = try include_string(@__MODULE__, """ @@ -228,7 +221,7 @@ let trace = try end @test trace[1].func === Symbol("top-level scope") @test trace[1].file === :a_filename - @test trace[1].line == 3 + @test trace[1].line == 2 end # issue #45171 diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 21567468ffe9e..b5fd95773e745 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -929,7 +929,7 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` close(in) close(err.in) txt = readline(err) - @test startswith(txt, r"ERROR: (syntax: incomplete|ParseError:)") + @test startswith(txt, "ERROR: syntax: incomplete") end # Issue #29855 diff --git a/test/show.jl b/test/show.jl index 25c5a49372054..f2c553b3ff49a 100644 --- a/test/show.jl +++ b/test/show.jl @@ -633,7 +633,7 @@ end @test_repr "::@m(x, y) + z" @test_repr "[@m(x) y z]" @test_repr "[@m(x) y; z]" -test_repr("let @m(x), y=z; end", true) +@test_repr "let @m(x), y=z; end" @test repr(:(@m x y)) == ":(#= $(@__FILE__):$(@__LINE__) =# @m x y)" @test string(:(@m x y)) == "#= $(@__FILE__):$(@__LINE__) =# @m x y" diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 13f2f5197187a..7151a4d4fd60a 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -250,6 +250,8 @@ end @test string(sym) == string(Char(0xdcdb)) @test String(sym) == string(Char(0xdcdb)) @test Meta.lower(Main, sym) === sym + @test Meta.parse(string(Char(0xe0080)," = 1"), 1, raise=false)[1] == + Expr(:error, "invalid character \"\Ue0080\" near column 1") end @testset "Symbol and gensym" begin @@ -759,6 +761,11 @@ function getData(dic) end @test getData(Dict()) == ",,,,,,,,,,,,,,,,,," +@testset "unrecognized escapes in string/char literals" begin + @test_throws Meta.ParseError Meta.parse("\"\\.\"") + @test_throws Meta.ParseError Meta.parse("\'\\.\'") +end + @testset "thisind" begin let strs = Any["∀α>β:α+1>β", s"∀α>β:α+1>β", SubString("123∀α>β:α+1>β123", 4, 18), diff --git a/test/syntax.jl b/test/syntax.jl index 4d1b167693adb..119f6d427a15a 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -5,29 +5,22 @@ using Random using Base: remove_linenums! -using_JuliaSyntax = parentmodule(Core._parse) != Core.Compiler - -macro test_parseerror(str, msg) - if using_JuliaSyntax - # Diagnostics are tested separately in JuliaSyntax - ex = :(@test_throws Meta.ParseError Meta.parse($(esc(str)))) +import Base.Meta.ParseError + +function parseall(str) + pos = firstindex(str) + exs = [] + while pos <= lastindex(str) + ex, pos = Meta.parse(str, pos) + push!(exs, ex) + end + if length(exs) == 0 + throw(ParseError("end of input")) + elseif length(exs) == 1 + return exs[1] else - ex = :(@test_throws Meta.ParseError($(esc(msg))) Meta.parse($(esc(str)))) + return Expr(:block, exs...) end - ex.args[2] = __source__ - return ex -end - -macro test_parseerror(str) - ex = :(@test_throws Meta.ParseError Meta.parse($(esc(str)))) - ex.args[2] = __source__ - return ex -end - -function parseall_nolines(str) - ex = Meta.parseall(str) - filter!(e->!(e isa LineNumberNode), ex.args) - return ex end # issue #9684 @@ -67,19 +60,19 @@ macro test999_str(args...); args; end @test test999"foo"123 == ("foo", 123) # issue #5997 -@test_parseerror ": x" -@test_parseerror """begin +@test_throws ParseError Meta.parse(": x") +@test_throws ParseError Meta.parse("""begin : - x""" -@test_parseerror "d[: 2]" + x""") +@test_throws ParseError Meta.parse("d[: 2]") # issue #6770 -@test_parseerror "x.3" +@test_throws ParseError Meta.parse("x.3") # issue #8763 -@test_parseerror "sqrt(16)2" -@test_parseerror "x' y" -@test_parseerror "x 'y" +@test_throws ParseError Meta.parse("sqrt(16)2") +@test_throws ParseError Meta.parse("x' y") +@test_throws ParseError Meta.parse("x 'y") @test Meta.parse("x'y") == Expr(:call, :*, Expr(Symbol("'"), :x), :y) # issue #18851 @@ -91,22 +84,22 @@ macro test999_str(args...); args; end @test Meta.parse("-2(m)") == Expr(:call, :*, -2, :m) # issue #8301 -@test_parseerror "&*s" +@test_throws ParseError Meta.parse("&*s") # issue #10677 -@test_parseerror "/1" -@test_parseerror "/pi" +@test_throws ParseError Meta.parse("/1") +@test_throws ParseError Meta.parse("/pi") @test Meta.parse("- = 2") == Expr(:(=), :(-), 2) @test Meta.parse("/ = 2") == Expr(:(=), :(/), 2) -@test_parseerror "< : 2" -@test_parseerror "+ : 2" -@test_parseerror "< :2" +@test_throws ParseError Meta.parse("< : 2") +@test_throws ParseError Meta.parse("+ : 2") +@test_throws ParseError Meta.parse("< :2") @test Meta.parse("+ :2") == Expr(:call, :(+), QuoteNode(2)) # issue #10900 -@test_parseerror "+=" -@test_parseerror "." -@test_parseerror "..." +@test_throws ParseError Meta.parse("+=") +@test_throws ParseError Meta.parse(".") +@test_throws ParseError Meta.parse("...") # issue #10901 @test Meta.parse("/([1], 1)[1]") == :(([1] / 1)[1]) @@ -159,35 +152,35 @@ macro test999_str(args...); args; end Expr(:., Expr(:$, :c), Expr(:$, :d)))) # fix pr #11338 and test for #11497 -@test parseall_nolines("using \$\na") == Expr(:toplevel, Expr(:using, Expr(:., :$)), :a) -@test parseall_nolines("using \$,\na") == Expr(:toplevel, Expr(:using, Expr(:., :$), Expr(:., :a))) -@test parseall_nolines("using &\na") == Expr(:toplevel, Expr(:using, Expr(:., :&)), :a) +@test parseall("using \$\na") == Expr(:block, Expr(:using, Expr(:., :$)), :a) +@test parseall("using \$,\na") == Expr(:using, Expr(:., :$), Expr(:., :a)) +@test parseall("using &\na") == Expr(:block, Expr(:using, Expr(:., :&)), :a) -@test parseall_nolines("a = &\nb") == Expr(:toplevel, Expr(:(=), :a, :&), :b) -@test parseall_nolines("a = \$\nb") == Expr(:toplevel, Expr(:(=), :a, :$), :b) -@test parseall_nolines(":(a = &\nb)") == Expr(:toplevel, Expr(:quote, Expr(:(=), :a, Expr(:&, :b)))) -@test parseall_nolines(":(a = \$\nb)") == Expr(:toplevel, Expr(:quote, Expr(:(=), :a, Expr(:$, :b)))) +@test parseall("a = &\nb") == Expr(:block, Expr(:(=), :a, :&), :b) +@test parseall("a = \$\nb") == Expr(:block, Expr(:(=), :a, :$), :b) +@test parseall(":(a = &\nb)") == Expr(:quote, Expr(:(=), :a, Expr(:&, :b))) +@test parseall(":(a = \$\nb)") == Expr(:quote, Expr(:(=), :a, Expr(:$, :b))) # issue 12027 - short macro name parsing vs _str suffix -@test parseall_nolines(""" - macro f(args...) end\n@f "macro argument" +@test parseall(""" + macro f(args...) end; @f "macro argument" """) == Expr(:toplevel, Expr(:macro, Expr(:call, :f, Expr(:..., :args)), Expr(:block, LineNumberNode(1, :none), LineNumberNode(1, :none))), - Expr(:macrocall, Symbol("@f"), LineNumberNode(2, :none), "macro argument")) + Expr(:macrocall, Symbol("@f"), LineNumberNode(1, :none), "macro argument")) # blocks vs. tuples @test Meta.parse("()") == Expr(:tuple) @test Meta.parse("(;)") == Expr(:tuple, Expr(:parameters)) @test Meta.parse("(;;)") == Expr(:block) @test Meta.parse("(;;;;)") == Expr(:block) -@test_parseerror "(,)" -@test_parseerror "(;,)" -@test_parseerror "(,;)" +@test_throws ParseError Meta.parse("(,)") +@test_throws ParseError Meta.parse("(;,)") +@test_throws ParseError Meta.parse("(,;)") # TODO: would be nice to make these errors, but needed to parse e.g. `(x;y,)->x` -#@test_parseerror "(1;2,)" -#@test_parseerror "(1;2,;)" -#@test_parseerror "(1;2,;3)" +#@test_throws ParseError Meta.parse("(1;2,)") +#@test_throws ParseError Meta.parse("(1;2,;)") +#@test_throws ParseError Meta.parse("(1;2,;3)") @test Meta.parse("(x;)") == Expr(:block, :x) @test Meta.parse("(;x)") == Expr(:tuple, Expr(:parameters, :x)) @test Meta.parse("(;x,)") == Expr(:tuple, Expr(:parameters, :x)) @@ -204,7 +197,7 @@ macro test999_str(args...); args; end @test Meta.parse("(x,a;y=1)") == Expr(:tuple, Expr(:parameters, Expr(:kw, :y, 1)), :x, :a) @test Meta.parse("(x,a;y=1,z=2)") == Expr(:tuple, Expr(:parameters, Expr(:kw,:y,1), Expr(:kw,:z,2)), :x, :a) @test Meta.parse("(a=1, b=2)") == Expr(:tuple, Expr(:(=), :a, 1), Expr(:(=), :b, 2)) -@test_parseerror "(1 2)" # issue #15248 +@test_throws ParseError Meta.parse("(1 2)") # issue #15248 @test Meta.parse("f(x;)") == Expr(:call, :f, Expr(:parameters), :x) @@ -275,16 +268,13 @@ end @test_throws BoundsError Meta.parse("x = 1", 7) # issue #14683 -@test_parseerror "'\\A\"'" +@test_throws ParseError Meta.parse("'\\A\"'") @test Meta.parse("'\"'") == Meta.parse("'\\\"'") == '"' == "\""[1] == '\42' # issue #24558 @test '\u2200' == "\u2200"[1] -if !using_JuliaSyntax - # This should be Expr(:incomplete) - @test_parseerror "f(2x for x=1:10, y" -end +@test_throws ParseError Meta.parse("f(2x for x=1:10, y") # issue #15223 call0(f) = f() @@ -320,6 +310,11 @@ let p = 15 @test 2p+1 == 31 # not a hex float literal end +macro test_parseerror(str, msg) + ex = :(@test_throws ParseError($(esc(msg))) Meta.parse($(esc(str)))) + ex.args[2] = __source__ + return ex +end @test_parseerror("0x", "invalid numeric constant \"0x\"") @test_parseerror("0b", "invalid numeric constant \"0b\"") @test_parseerror("0o", "invalid numeric constant \"0o\"") @@ -327,8 +322,9 @@ end @test_parseerror("0x1.0p", "invalid numeric constant \"0x1.0\"") # issue #15798 -# lowering preserves Expr(:error) -@test Meta.lower(Main, Expr(:error, "no")) == Expr(:error, "no") +@test Meta.lower(Main, Base.parse_input_line(""" + try = "No" + """)) == Expr(:error, "unexpected \"=\"") # issue #19861 make sure macro-expansion happens in the newest world for top-level expression @test eval(Base.parse_input_line(""" @@ -372,9 +368,9 @@ add_method_to_glob_fn!() @test f15844(Int64(1)) == 3 # issue #15661 -@test_parseerror "function catch() end" -@test_parseerror "function end() end" -@test_parseerror "function finally() end" +@test_throws ParseError Meta.parse("function catch() end") +@test_throws ParseError Meta.parse("function end() end") +@test_throws ParseError Meta.parse("function finally() end") # PR #16170 @test Meta.lower(Main, Meta.parse("true(x) = x")) == Expr(:error, "invalid function name \"true\"") @@ -425,18 +421,18 @@ end :y)) # test that pre 0.5 deprecated syntax is a parse error -@test_parseerror "Int [1,2,3]" -@test_parseerror "Int [x for x in 1:10]" -@test_parseerror "foo (x) = x" -@test_parseerror "foo {T<:Int}(x::T) = x" +@test_throws ParseError Meta.parse("Int [1,2,3]") +@test_throws ParseError Meta.parse("Int [x for x in 1:10]") +@test_throws ParseError Meta.parse("foo (x) = x") +@test_throws ParseError Meta.parse("foo {T<:Int}(x::T) = x") -@test_parseerror "Foo .bar" +@test_throws ParseError Meta.parse("Foo .bar") -@test_parseerror "import x .y" -@test_parseerror "using x .y" +@test_throws ParseError Meta.parse("import x .y") +@test_throws ParseError Meta.parse("using x .y") -@test_parseerror "--x" -@test_parseerror "stagedfunction foo(x); end" +@test_throws ParseError Meta.parse("--x") +@test_throws ParseError Meta.parse("stagedfunction foo(x); end") @test Meta.parse("A=>B") == Expr(:call, :(=>), :A, :B) @@ -452,7 +448,7 @@ end @test Meta.parse("[a,;c]") == Expr(:vect, Expr(:parameters, :c), :a) @test Meta.parse("a[b,c;d]") == Expr(:ref, :a, Expr(:parameters, :d), :b, :c) @test Meta.parse("a[b,;d]") == Expr(:ref, :a, Expr(:parameters, :d), :b) -@test_parseerror "[a,;,b]" +@test_throws ParseError Meta.parse("[a,;,b]") @test Meta.parse("{a,b;c}") == Expr(:braces, Expr(:parameters, :c), :a, :b) @test Meta.parse("{a,;c}") == Expr(:braces, Expr(:parameters, :c), :a) @test Meta.parse("a{b,c;d}") == Expr(:curly, :a, Expr(:parameters, :d), :b, :c) @@ -538,13 +534,10 @@ for (str, tag) in Dict("" => :none, "\"" => :string, "#=" => :comment, "'" => :c "let;" => :block, "for i=1;" => :block, "function f();" => :block, "f() do x;" => :block, "module X;" => :block, "mutable struct X;" => :block, "struct X;" => :block, "(" => :other, "[" => :other, - "for" => :other, "function" => :other, + "begin" => :other, "quote" => :other, + "let" => :other, "for" => :other, "function" => :other, "f() do" => :other, "module" => :other, "mutable struct" => :other, - "struct" => :other, - "quote" => using_JuliaSyntax ? :block : :other, - "let" => using_JuliaSyntax ? :block : :other, - "begin" => using_JuliaSyntax ? :block : :other, - ) + "struct" => :other) @test Base.incomplete_tag(Meta.parse(str, raise=false)) == tag end @@ -629,7 +622,7 @@ end # issue 10046 for op in ["+", "-", "\$", "|", ".+", ".-", "*", ".*"] - @test_parseerror "$op in [+, -]" + @test_throws ParseError Meta.parse("$op in [+, -]") end # issue #17701 @@ -641,7 +634,7 @@ end # PR #15592 let str = "[1] [2]" - @test_parseerror str + @test_throws ParseError Meta.parse(str) end # issue 15896 and PR 15913 @@ -1004,14 +997,14 @@ end @test Test21604.X(1.0) === Test21604.X(1.0) # issue #20575 -@test_parseerror "\"a\"x" -@test_parseerror "\"a\"begin end" -@test_parseerror "\"a\"begin end\"b\"" +@test_throws ParseError Meta.parse("\"a\"x") +@test_throws ParseError Meta.parse("\"a\"begin end") +@test_throws ParseError Meta.parse("\"a\"begin end\"b\"") # issue #16427 -@test_parseerror "for i=1:1 end(3)" -@test_parseerror "begin end(3)" -@test_parseerror "while false end(3)" +@test_throws ParseError Meta.parse("for i=1:1 end(3)") +@test_throws ParseError Meta.parse("begin end(3)") +@test_throws ParseError Meta.parse("while false end(3)") # comment 298107224 on pull #21607 module Test21607 @@ -1072,7 +1065,7 @@ end === (3, String) @test Meta.parse("3 +⁽¹⁾ 4") == Expr(:call, :+⁽¹⁾, 3, 4) @test Meta.parse("3 +₍₀₎ 4") == Expr(:call, :+₍₀₎, 3, 4) for bad in ('=', '$', ':', "||", "&&", "->", "<:") - @test_parseerror "3 $(bad)⁽¹⁾ 4" + @test_throws ParseError Meta.parse("3 $(bad)⁽¹⁾ 4") end @test Base.operator_precedence(:+̂) == Base.operator_precedence(:+) @@ -1087,20 +1080,20 @@ end Expr(:tuple, :x, :y), Expr(:tuple, 1, 2))) -@test_parseerror "[2for i=1:10]" -@test_parseerror "[1 for i in 1:2for j in 2]" -@test_parseerror "(1 for i in 1:2for j in 2)" +@test_throws ParseError Meta.parse("[2for i=1:10]") +@test_throws ParseError Meta.parse("[1 for i in 1:2for j in 2]") +@test_throws ParseError Meta.parse("(1 for i in 1:2for j in 2)") # issue #20441 -@test_parseerror "[x.2]" -@test_parseerror "x.2" +@test_throws ParseError Meta.parse("[x.2]") +@test_throws ParseError Meta.parse("x.2") @test Meta.parse("[x;.2]") == Expr(:vcat, :x, 0.2) # issue #22840 @test Meta.parse("[:a :b]") == Expr(:hcat, QuoteNode(:a), QuoteNode(:b)) # issue #22868 -@test_parseerror "x@time 2" -@test_parseerror "@ time" +@test_throws ParseError Meta.parse("x@time 2") +@test_throws ParseError Meta.parse("@ time") # issue #7479 @test Meta.lower(Main, Meta.parse("(true &&& false)")) == Expr(:error, "invalid syntax &false") @@ -1109,9 +1102,9 @@ end @test Meta.lower(Main, :(&(1, 2))) == Expr(:error, "invalid syntax &(1, 2)") # if an indexing expression becomes a cat expression, `end` is not special -@test_parseerror "a[end end]" -@test_parseerror "a[end;end]" -#@test_parseerror "a[end;]" # this is difficult to fix +@test_throws ParseError Meta.parse("a[end end]") +@test_throws ParseError Meta.parse("a[end;end]") +#@test_throws ParseError Meta.parse("a[end;]") # this is difficult to fix let a = rand(8), i = 3 @test a[[1:i-1; i+1:end]] == a[[1,2,4,5,6,7,8]] end @@ -1122,12 +1115,12 @@ end end for i = 1:5] == fill(nothing, 5) # issue #18912 -@test_parseerror "(::)" +@test_throws ParseError Meta.parse("(::)") @test Meta.parse(":(::)") == QuoteNode(Symbol("::")) -@test_parseerror "f(::) = ::" +@test_throws ParseError Meta.parse("f(::) = ::") @test Meta.parse("(::A)") == Expr(Symbol("::"), :A) -@test_parseerror "(::, 1)" -@test_parseerror "(1, ::)" +@test_throws ParseError Meta.parse("(::, 1)") +@test_throws ParseError Meta.parse("(1, ::)") # issue #18650 let ex = Meta.parse("maximum(@elapsed sleep(1) for k = 1:10)") @@ -1199,10 +1192,10 @@ M24289.@m24289 # parsing numbers with _ and . @test Meta.parse("1_2.3_4") == 12.34 -@test_parseerror "1._" -@test_parseerror "1._5" -@test_parseerror "1e.3" -@test_parseerror "1e3." +@test_throws ParseError Meta.parse("1._") +@test_throws ParseError Meta.parse("1._5") +@test_throws ParseError Meta.parse("1e.3") +@test_throws ParseError Meta.parse("1e3.") @test Meta.parse("2e_1") == Expr(:call, :*, 2, :e_1) # issue #17705 @test Meta.parse("2e3_") == Expr(:call, :*, 2e3, :_) @@ -1268,10 +1261,8 @@ end @test raw"x \\\ y" == "x \\\\\\ y" end -@test_parseerror("f(x::V) where {V) = x", - "expected \"}\" or separator in arguments to \"{ }\"; got \"V)\"") -@test_parseerror("[1)", - "expected \"]\" or separator in arguments to \"[ ]\"; got \"1)\"") +@test_throws ParseError("expected \"}\" or separator in arguments to \"{ }\"; got \"V)\"") Meta.parse("f(x::V) where {V) = x") +@test_throws ParseError("expected \"]\" or separator in arguments to \"[ ]\"; got \"1)\"") Meta.parse("[1)") # issue #9972 @test Meta.lower(@__MODULE__, :(f(;3))) == Expr(:error, "invalid keyword argument syntax \"3\"") @@ -1319,7 +1310,7 @@ let getindex = 0, setindex! = 1, colon = 2, vcat = 3, hcat = 4, hvcat = 5 end # issue #25020 -@test_parseerror "using Colors()" +@test_throws ParseError Meta.parse("using Colors()") let ex = Meta.parse("md\"x\" f(x) = x", 1)[1] # custom string literal is not a docstring @@ -1373,18 +1364,18 @@ end @test Meta.parse("-(x;;;)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:block, :x), 2)) @test Meta.parse("+((1,2))") == Expr(:call, :+, Expr(:tuple, 1, 2)) -@test_parseerror "1 -+ (a=1, b=2)" "space before \"(\" not allowed in \"+ (\" at none:1" +@test_throws ParseError("space before \"(\" not allowed in \"+ (\" at none:1") Meta.parse("1 -+ (a=1, b=2)") # issue #29781 -@test_parseerror "sin. (1)" "space before \"(\" not allowed in \"sin. (\" at none:1" +@test_throws ParseError("space before \"(\" not allowed in \"sin. (\" at none:1") Meta.parse("sin. (1)") # Parser errors for disallowed space contain line numbers -@test_parseerror "\nf() [i]" "space before \"[\" not allowed in \"f() [\" at none:2" -@test_parseerror "\nf() (i)" "space before \"(\" not allowed in \"f() (\" at none:2" -@test_parseerror "\nf() .i" "space before \".\" not allowed in \"f() .\" at none:2" -@test_parseerror "\nf() {i}" "space before \"{\" not allowed in \"f() {\" at none:2" -@test_parseerror "\n@ m" "space before \"m\" not allowed in \"@ m\" at none:2" -@test_parseerror "\nusing a .b" "space before \".\" not allowed in \"a .\" at none:2" -@test_parseerror "\nusing a .b" "space before \".\" not allowed in \"a .\" at none:2" -@test_parseerror "\n+ (x, y)" "space before \"(\" not allowed in \"+ (\" at none:2" +@test_throws ParseError("space before \"[\" not allowed in \"f() [\" at none:2") Meta.parse("\nf() [i]") +@test_throws ParseError("space before \"(\" not allowed in \"f() (\" at none:2") Meta.parse("\nf() (i)") +@test_throws ParseError("space before \".\" not allowed in \"f() .\" at none:2") Meta.parse("\nf() .i") +@test_throws ParseError("space before \"{\" not allowed in \"f() {\" at none:2") Meta.parse("\nf() {i}") +@test_throws ParseError("space before \"m\" not allowed in \"@ m\" at none:2") Meta.parse("\n@ m") +@test_throws ParseError("space before \".\" not allowed in \"a .\" at none:2") Meta.parse("\nusing a .b") +@test_throws ParseError("space before \".\" not allowed in \"a .\" at none:2") Meta.parse("\nusing a .b") +@test_throws ParseError("space before \"(\" not allowed in \"+ (\" at none:2") Meta.parse("\n+ (x, y)") @test Meta.parse("1 -+(a=1, b=2)") == Expr(:call, :-, 1, Expr(:call, :+, Expr(:kw, :a, 1), Expr(:kw, :b, 2))) @@ -1406,7 +1397,7 @@ end @test Meta.parse("-√2") == Expr(:call, :-, Expr(:call, :√, 2)) @test Meta.parse("√3x^2") == Expr(:call, :*, Expr(:call, :√, 3), Expr(:call, :^, :x, 2)) @test Meta.parse("-3x^2") == Expr(:call, :*, -3, Expr(:call, :^, :x, 2)) -@test_parseerror "2!3" +@test_throws ParseError Meta.parse("2!3") # issue #27914 @test Meta.parse("2f(x)") == Expr(:call, :*, 2, Expr(:call, :f, :x)) @@ -1416,7 +1407,7 @@ end @test Meta.parse("2(x)") == Expr(:call, :*, 2, :x) @test Meta.parse("2(x)y") == Expr(:call, :*, 2, :x, :y) -@test_parseerror "a.: b" +@test_throws ParseError Meta.parse("a.: b") @test Meta.parse("a.:end") == Expr(:., :a, QuoteNode(:end)) @test Meta.parse("a.:catch") == Expr(:., :a, QuoteNode(:catch)) @test Meta.parse("a.end") == Expr(:., :a, QuoteNode(:end)) @@ -1432,7 +1423,7 @@ let len = 10 end # Module name cannot be a reserved word. -@test_parseerror "module module end" +@test_throws ParseError Meta.parse("module module end") @test Meta.lower(@__MODULE__, :(global true)) == Expr(:error, "invalid syntax in \"global\" declaration") @test Meta.lower(@__MODULE__, :(let ccall end)) == Expr(:error, "invalid identifier name \"ccall\"") @@ -1449,7 +1440,7 @@ end # issue #27690 # previously, this was allowed since it thought `end` was being used for indexing. # however the quote should disable that context. -@test_parseerror "Any[:(end)]" +@test_throws ParseError Meta.parse("Any[:(end)]") # issue #17781 let ex = Meta.lower(@__MODULE__, Meta.parse(" @@ -1680,20 +1671,20 @@ let x = @macroexpand @foo28244(var"let") end # #16356 -@test_parseerror "0xapi" +@test_throws ParseError Meta.parse("0xapi") # #22523 #22712 -@test_parseerror "a?b:c" -@test_parseerror "a ?b:c" -@test_parseerror "a ? b:c" -@test_parseerror "a ? b :c" -@test_parseerror "?" +@test_throws ParseError Meta.parse("a?b:c") +@test_throws ParseError Meta.parse("a ?b:c") +@test_throws ParseError Meta.parse("a ? b:c") +@test_throws ParseError Meta.parse("a ? b :c") +@test_throws ParseError Meta.parse("?") # #13079 @test Meta.parse("1<<2*3") == :((1<<2)*3) # #19987 -@test_parseerror "try ; catch f() ; end" +@test_throws ParseError Meta.parse("try ; catch f() ; end") # #23076 @test :([1,2;]) == Expr(:vect, Expr(:parameters), 1, 2) @@ -1730,8 +1721,8 @@ end @test Meta.lower(@__MODULE__, :(f(x) = (y = x + 1; ccall((:a, y), Cvoid, ())))) == Expr(:error, "ccall function name and library expression cannot reference local variables") -@test_parseerror "x.'" -@test_parseerror "0.+1" +@test_throws ParseError Meta.parse("x.'") +@test_throws ParseError Meta.parse("0.+1") # #24221 @test Meta.isexpr(Meta.lower(@__MODULE__, :(a=_)), :error) @@ -1825,7 +1816,7 @@ end @test Meta.parse("1⁝2") == Expr(:call, :⁝, 1, 2) @test Meta.parse("1..2") == Expr(:call, :.., 1, 2) # we don't parse chains of these since the associativity and meaning aren't clear -@test_parseerror "1..2..3" +@test_throws ParseError Meta.parse("1..2..3") # issue #30048 @test Meta.isexpr(Meta.lower(@__MODULE__, :(for a in b @@ -1999,9 +1990,9 @@ end @test Meta.parse("var\"#\"") === Symbol("#") @test Meta.parse("var\"true\"") === Symbol("true") @test Meta.parse("var\"false\"") === Symbol("false") -@test_parseerror "var\"#\"x" # Reject string macro-like suffix -@test_parseerror "var \"#\"" -@test_parseerror "var\"for\" i = 1:10; end" +@test_throws ParseError Meta.parse("var\"#\"x") # Reject string macro-like suffix +@test_throws ParseError Meta.parse("var \"#\"") +@test_throws ParseError Meta.parse("var\"for\" i = 1:10; end") # A few cases which would be ugly to deal with if var"#" were a string macro: @test Meta.parse("var\"#\".var\"a-b\"") == Expr(:., Symbol("#"), QuoteNode(Symbol("a-b"))) @test Meta.parse("export var\"#\"") == Expr(:export, Symbol("#")) @@ -2226,7 +2217,7 @@ end end # line break in : expression disallowed -@test_parseerror "[1 :\n2] == [1:2]" +@test_throws Meta.ParseError Meta.parse("[1 :\n2] == [1:2]") # added ⟂ to operator precedence (#24404) @test Meta.parse("a ⟂ b ⟂ c") == Expr(:comparison, :a, :⟂, :b, :⟂, :c) @@ -2247,8 +2238,7 @@ end end # only allow certain characters after interpolated vars (#25231) -@test_parseerror("\"\$x෴ \"", - "interpolated variable \$x ends with invalid character \"෴\"; use \"\$(x)\" instead.") +@test Meta.parse("\"\$x෴ \"",raise=false) == Expr(:error, "interpolated variable \$x ends with invalid character \"෴\"; use \"\$(x)\" instead.") @test Base.incomplete_tag(Meta.parse("\"\$foo", raise=false)) === :string @testset "issue #30341" begin @@ -2287,11 +2277,14 @@ end err = Expr( :error, + "\":\" in \"$imprt\" syntax can only be used when importing a single module. " * + "Split imports into multiple lines." ) - @test_parseerror("$imprt A, B: x, y", - "\":\" in \"$imprt\" syntax can only be used when importing a single module. Split imports into multiple lines.") - @test_parseerror("$imprt A: x, B: y", - "\":\" in \"$imprt\" syntax can only be used when importing a single module. Split imports into multiple lines.") + ex = Meta.parse("$imprt A, B: x, y", raise=false) + @test ex == err + + ex = Meta.parse("$imprt A: x, B: y", raise=false) + @test ex == err end end @@ -2311,31 +2304,24 @@ let exc = try eval(:(f(x,x)=1)) catch e ; e ; end @test !occursin("incorrect_file", exc.msg) end -@testset "issue #34967" begin - @test_parseerror "#\xf5b\nx" "invalid UTF-8 sequence" - - # Test line UTF-8 errors with line numbers - let ex = Meta.parseall("x\n#\xf5b\ny") - @test Meta.isexpr(ex, :toplevel, 4) && Meta.isexpr(last(ex.args), :error) - @test ex.args[3] == LineNumberNode(2,:none) - end - let ex = Meta.parseall("x\xf5\n#\xf5b\ny") - @test Meta.isexpr(ex, :toplevel, 2) && Meta.isexpr(last(ex.args), :error) - @test ex.args[1] == LineNumberNode(1,:none) - end - let ex = Meta.parseall("#line1\n#line2\n#\xf5b\ny") - @test Meta.isexpr(ex, :toplevel, 2) && Meta.isexpr(last(ex.args), :error) - @test ex.args[1] == LineNumberNode(3,:none) - end -end - -@test_parseerror "aa\u200b_" "invisible character \\u200b near column 3" -@test_parseerror "aa\UE0080" "invalid character \"\Ue0080\" near column 3" - -@testset "unrecognized escapes in string/char literals" begin - @test_parseerror "\"\\.\"" - @test_parseerror "\'\\.\'" -end +# issue #34967 +@test_throws LoadError("string", 2, ErrorException("syntax: invalid UTF-8 sequence")) include_string(@__MODULE__, + "x34967 = 1\n# Halloa\xf5b\nx34967 = 2") +@test x34967 == 1 +@test_throws LoadError("string", 1, ErrorException("syntax: invalid UTF-8 sequence")) include_string(@__MODULE__, + "x\xf5 = 3\n# Halloa\xf5b\nx34967 = 4") +@test_throws LoadError("string", 3, ErrorException("syntax: invalid UTF-8 sequence")) include_string(@__MODULE__, + """ + # line 1 + # line 2 + # Hello\xf5b + x34967 = 6 + """) + +@test Meta.parse("aa\u200b_", raise=false) == + Expr(:error, "invisible character \\u200b near column 3") +@test Meta.parse("aa\UE0080", raise=false) == + Expr(:error, "invalid character \"\Ue0080\" near column 3") # issue #31238 a31238, b31238 = let x @@ -2404,8 +2390,8 @@ end @test x == 6 # issue #36196 -@test_parseerror "(for i=1; println())" "\"for\" at none:1 expected \"end\", got \")\"" -@test_parseerror "(try i=1; println())" "\"try\" at none:1 expected \"end\", got \")\"" +@test_throws ParseError("\"for\" at none:1 expected \"end\", got \")\"") Meta.parse("(for i=1; println())") +@test_throws ParseError("\"try\" at none:1 expected \"end\", got \")\"") Meta.parse("(try i=1; println())") # issue #36272 macro m36272() @@ -2452,10 +2438,10 @@ end let (-->) = (+) @test (40 --> 2) == 42 end -@test_parseerror("1<---2", "invalid operator \"<---\"") -@test_parseerror("1 .<--- 2", "invalid operator \".<---\"") -@test_parseerror("a---b", "invalid operator \"--\"") -@test_parseerror("a.---b", "invalid operator \".--\"") +@test_throws ParseError("invalid operator \"<---\"") Meta.parse("1<---2") +@test_throws ParseError("invalid operator \".<---\"") Meta.parse("1 .<--- 2") +@test_throws ParseError("invalid operator \"--\"") Meta.parse("a---b") +@test_throws ParseError("invalid operator \".--\"") Meta.parse("a.---b") # issue #37228 # NOTE: the `if` needs to be at the top level @@ -2490,14 +2476,15 @@ end @test :(if true 'a' else 1 end) == Expr(:if, true, quote 'a' end, quote 1 end) # issue #37664 -@test_parseerror("a b", "extra token \"b\" after end of expression") -@test_parseerror("a#==#b", "extra token \"b\" after end of expression") -@test_parseerror("a #==#b", "extra token \"b\" after end of expression") -@test_parseerror("a#==# b", "extra token \"b\" after end of expression") -@test_parseerror("1 2", "extra token \"2\" after end of expression") -@test_parseerror("1#==#2", "extra token \"2\" after end of expression") -@test_parseerror("1 #==#2", "extra token \"2\" after end of expression") -@test_parseerror("1#==# 2", "extra token \"2\" after end of expression") +@test_throws ParseError("extra token \"b\" after end of expression") Meta.parse("a b") +@test_throws ParseError("extra token \"b\" after end of expression") Meta.parse("a#==#b") +@test_throws ParseError("extra token \"b\" after end of expression") Meta.parse("a #==#b") +@test_throws ParseError("extra token \"b\" after end of expression") Meta.parse("a#==# b") + +@test_throws ParseError("extra token \"2\" after end of expression") Meta.parse("1 2") +@test_throws ParseError("extra token \"2\" after end of expression") Meta.parse("1#==#2") +@test_throws ParseError("extra token \"2\" after end of expression") Meta.parse("1 #==#2") +@test_throws ParseError("extra token \"2\" after end of expression") Meta.parse("1#==# 2") @test size([1#==#2#==#3]) == size([1 2 3]) @test size([1#==#2#==#3]) == size([1 2 3]) # tabs @@ -2520,7 +2507,9 @@ end Meta.parse("if#==#x0#==#y+1#==#else#==#z#==#end") @test Meta.parse("function(x) x end") == Meta.parse("function(x)#==#x#==#end") @test Meta.parse("a ? b : c") == Meta.parse("a#==#?#==#b#==#:#==#c") -@test_parseerror("f#==#(x)=x", "space before \"(\" not allowed in \"f (\" at none:1") +@test_throws ParseError("space before \"(\" not allowed in \"f (\" at none:1") begin + Meta.parse("f#==#(x)=x") +end @test Meta.parse("try f() catch e g() finally h() end") == Meta.parse("try#==#f()#==#catch#==#e#==#g()#==#finally#==#h()#==#end") @test Meta.parse("@m a b") == Meta.parse("@m#==#a#==#b") @@ -2552,11 +2541,11 @@ end @test B37890(1.0, 2.0f0) isa B37890{Int, Int8} # import ... as -@test_parseerror("using A as B", "invalid syntax \"using A as ...\"") -@test_parseerror("using A.b as B", "invalid syntax \"using A.b as ...\"") -@test_parseerror("using X, A.b as B", "invalid syntax \"using A.b as ...\"") -@test_parseerror("import A as B: c", "invalid syntax \"import A as B:\"") -@test_parseerror("import A.b as B: c", "invalid syntax \"import A.b as B:\"") +@test_throws ParseError("invalid syntax \"using A as ...\"") Meta.parse("using A as B") +@test_throws ParseError("invalid syntax \"using A.b as ...\"") Meta.parse("using A.b as B") +@test_throws ParseError("invalid syntax \"using A.b as ...\"") Meta.parse("using X, A.b as B") +@test_throws ParseError("invalid syntax \"import A as B:\"") Meta.parse("import A as B: c") +@test_throws ParseError("invalid syntax \"import A.b as B:\"") Meta.parse("import A.b as B: c") module TestImportAs using Test @@ -2595,9 +2584,7 @@ import .Mod2.y as y2 @test y2 == 2 @test !@isdefined(y) -# Test that eval rejects the invalid syntax `import .Mod.x as (a.b)` -@test_throws ErrorException eval( - Expr(:import, Expr(:as, Expr(:., :., :Mod, :x), Expr(:., :a, QuoteNode(:b))))) +@test_throws ErrorException eval(:(import .Mod.x as (a.b))) import .Mod.maybe_undef as mu @test_throws UndefVarError mu @@ -2675,10 +2662,10 @@ end @test Meta.isexpr(Meta.parse(""" f(i for i in 1:3)""").args[2], :generator) - @test_parseerror """ + @test_throws Meta.ParseError Meta.parse(""" for i in 1:3 - end""" + end""") end # PR #37973 @@ -2833,7 +2820,7 @@ end Expr(:nrow, 1, Expr(:row, 0, 9, 3), Expr(:row, 4, 5, 4))) @test :([1 ; 2 ;; 3 ; 4]) == Expr(:ncat, 2, Expr(:nrow, 1, 1, 2), Expr(:nrow, 1, 3, 4)) - @test_parseerror "[1 2 ;; 3 4]" # cannot mix spaces and ;; except as line break + @test_throws ParseError Meta.parse("[1 2 ;; 3 4]") # cannot mix spaces and ;; except as line break @test :([1 2 ;; 3 4]) == :([1 2 3 4]) @test :([1 2 ;; @@ -2843,8 +2830,8 @@ end @test Meta.parse("[1;\n\n]") == :([1;]) @test Meta.parse("[1\n;]") == :([1;]) # semicolons following a linebreak are fine @test Meta.parse("[1\n;;; 2]") == :([1;;; 2]) - @test_parseerror "[1;\n;2]" # semicolons cannot straddle a line break - @test_parseerror "[1; ;2]" # semicolons cannot be separated by a space + @test_throws ParseError Meta.parse("[1;\n;2]") # semicolons cannot straddle a line break + @test_throws ParseError Meta.parse("[1; ;2]") # semicolons cannot be separated by a space end # issue #25652 @@ -3117,10 +3104,10 @@ end @test fails(error) @test !fails(() -> 1 + 2) - @test_parseerror "try foo() else bar() end" - @test_parseerror "try foo() else bar() catch; baz() end" - @test_parseerror "try foo() catch; baz() finally foobar() else bar() end" - @test_parseerror "try foo() finally foobar() else bar() catch; baz() end" + @test_throws ParseError Meta.parse("try foo() else bar() end") + @test_throws ParseError Meta.parse("try foo() else bar() catch; baz() end") + @test_throws ParseError Meta.parse("try foo() catch; baz() finally foobar() else bar() end") + @test_throws ParseError Meta.parse("try foo() finally foobar() else bar() catch; baz() end") err = try try @@ -3185,23 +3172,23 @@ end @test x == 1 end -@test_parseerror """ +@test_throws ParseError Meta.parse(""" function checkUserAccess(u::User) if u.accessLevel != "user\u202e \u2066# users are not allowed\u2069\u2066" return true end return false end -""" +""") -@test_parseerror """ +@test_throws ParseError Meta.parse(""" function checkUserAccess(u::User) #=\u202e \u2066if (u.isAdmin)\u2069 \u2066 begin admins only =# return true #= end admin only \u202e \u2066end\u2069 \u2066=# return false end -""" +""") @testset "empty nd arrays" begin @test :([]) == Expr(:vect) @@ -3232,9 +3219,9 @@ end ;; ]) == Expr(:ncat, 2) - @test_parseerror "[; ;]" - @test_parseerror "[;; ;]" - @test_parseerror "[;\n;]" + @test_throws ParseError Meta.parse("[; ;]") + @test_throws ParseError Meta.parse("[;; ;]") + @test_throws ParseError Meta.parse("[;\n;]") end @test Meta.parseatom("@foo", 1; filename="foo", lineno=7) == (Expr(:macrocall, :var"@foo", LineNumberNode(7, :foo)), 5) @@ -3428,12 +3415,14 @@ f45162(f) = f(x=1) @test first(methods(f45162)).called != 0 # issue #45024 -@test_parseerror "const x" "expected assignment after \"const\"" -@test_parseerror "const x::Int" "expected assignment after \"const\"" +@test_throws ParseError("expected assignment after \"const\"") Meta.parse("const x") +@test_throws ParseError("expected assignment after \"const\"") Meta.parse("const x::Int") # these cases have always been caught during lowering, since (const (global x)) is not # ambiguous with the lowered form (const x), but that could probably be changed. -@test Meta.lower(@__MODULE__, Expr(:const, Expr(:global, :x))) == Expr(:error, "expected assignment after \"const\"") -@test Meta.lower(@__MODULE__, Expr(:const, Expr(:global, Expr(:(::), :x, :Int)))) == Expr(:error, "expected assignment after \"const\"") +@test Meta.lower(@__MODULE__, :(global const x)) == Expr(:error, "expected assignment after \"const\"") +@test Meta.lower(@__MODULE__, :(global const x::Int)) == Expr(:error, "expected assignment after \"const\"") +@test Meta.lower(@__MODULE__, :(const global x)) == Expr(:error, "expected assignment after \"const\"") +@test Meta.lower(@__MODULE__, :(const global x::Int)) == Expr(:error, "expected assignment after \"const\"") @testset "issue 25072" begin @test '\xc0\x80' == reinterpret(Char, 0xc0800000)