Skip to content

Commit

Permalink
Make static_show() print valid identifiers (#38049)
Browse files Browse the repository at this point in the history
  • Loading branch information
NHDaly authored Oct 26, 2020
1 parent da0d968 commit 031c877
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 33 deletions.
118 changes: 85 additions & 33 deletions src/rtutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,43 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROO
return (jl_value_t*)dt;
}

static int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname_out)
{
jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL;
*globname_out = globname;
int globfunc = 0;
if (globname && !strchr(jl_symbol_name(globname), '#') &&
!strchr(jl_symbol_name(globname), '@') && dv->name->module &&
jl_binding_resolved_p(dv->name->module, globname)) {
jl_binding_t *b = jl_get_module_binding(dv->name->module, globname);
// The `||` makes this function work for both function instances and function types.
if (b && b->value && (b->value == v || jl_typeof(b->value) == v)) {
globfunc = 1;
}
}
return globfunc;
}

static size_t jl_static_show_x_sym_escaped(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT
{
size_t n = 0;

char *sn = jl_symbol_name(name);
int hidden = 0;
if (!(jl_is_identifier(sn) || jl_is_operator(sn))) {
hidden = 1;
}

if (hidden) {
n += jl_printf(out, "var\"");
}
n += jl_printf(out, "%s", sn);
if (hidden) {
n += jl_printf(out, "\"");
}
return n;
}

// `v` might be pointing to a field inlined in a structure therefore
// `jl_typeof(v)` may not be the same with `vt` and only `vt` should be
// used to determine the type of the value.
Expand Down Expand Up @@ -670,48 +707,32 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt
n += jl_printf(out, "UnionAll");
}
else if (vt == jl_datatype_type) {
// typeof(v) == DataType, so v is a Type object.
// Types are printed as a fully qualified name, with parameters, e.g.
// `Base.Set{Int}`, and function types are printed as e.g. `typeof(Main.f)`
jl_datatype_t *dv = (jl_datatype_t*)v;
jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL;
int globfunc = 0;
if (globname && !strchr(jl_symbol_name(globname), '#') &&
!strchr(jl_symbol_name(globname), '@') && dv->name->module &&
jl_binding_resolved_p(dv->name->module, globname)) {
jl_binding_t *b = jl_get_module_binding(dv->name->module, globname);
if (b && b->value && jl_typeof(b->value) == v)
globfunc = 1;
}
jl_sym_t *globname;
int globfunc = is_globfunction(v, dv, &globname);
jl_sym_t *sym = globfunc ? globname : dv->name->name;
char *sn = jl_symbol_name(sym);
int hidden = !globfunc && strchr(sn, '#');
size_t i = 0;
int quote = 0;
if (hidden) {
n += jl_printf(out, "getfield(");
}
else if (globfunc) {
size_t quote = 0;
if (globfunc) {
n += jl_printf(out, "typeof(");
}
if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) {
n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth);
if (!hidden) {
n += jl_printf(out, ".");
if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) {
n += jl_printf(out, ":(");
quote = 1;
}
n += jl_printf(out, ".");
size_t i = 0;
if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) {
n += jl_printf(out, ":(");
quote = 1;
}
}
if (hidden) {
n += jl_printf(out, ", Symbol(\"");
n += jl_printf(out, "%s", sn);
n += jl_printf(out, "\"))");
}
else {
n += jl_printf(out, "%s", sn);
if (globfunc) {
n += jl_static_show_x_sym_escaped(out, sym);
if (globfunc) {
n += jl_printf(out, ")");
if (quote) {
n += jl_printf(out, ")");
if (quote)
n += jl_printf(out, ")");
}
}
if (dv->parameters && (jl_value_t*)dv != dv->name->wrapper &&
Expand Down Expand Up @@ -832,7 +853,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt
n += jl_printf(out, ")");
n += jl_printf(out, "<:");
}
n += jl_printf(out, "%s", jl_symbol_name(var->name));
n += jl_static_show_x_sym_escaped(out, var->name);
if (showbounds && (ub != (jl_value_t*)jl_any_type || lb != jl_bottom_type)) {
// show type-var upper bound if it is defined, or if we showed the lower bound
int ua = jl_is_unionall(ub);
Expand Down Expand Up @@ -978,7 +999,38 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt
n += jl_static_show_x(out, *(jl_value_t**)v, depth);
n += jl_printf(out, ")");
}
else if (jl_function_type && jl_isa(v, (jl_value_t*)jl_function_type)) {
// v is function instance (an instance of a Function type).
jl_datatype_t *dv = (jl_datatype_t*)vt;
jl_sym_t *sym = dv->name->mt->name;
char *sn = jl_symbol_name(sym);

jl_sym_t *globname;
int globfunc = is_globfunction(v, dv, &globname);
int quote = 0;
if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) {
n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth);
n += jl_printf(out, ".");

size_t i = 0;
if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) {
n += jl_printf(out, ":(");
quote = 1;
}
}

n += jl_static_show_x_sym_escaped(out, sym);

if (globfunc) {
if (quote) {
n += jl_printf(out, ")");
}
}
}
else if (jl_datatype_type && jl_is_datatype(vt)) {
// typeof(v) isa DataType, so v is an *instance of* a type that is a Datatype,
// meaning v is e.g. an instance of a struct. These are printed as a call to a
// type constructor, such as e.g. `Base.UnitRange{Int64}(start=1, stop=2)`
int istuple = jl_is_tuple_type(vt), isnamedtuple = jl_is_namedtuple_type(vt);
size_t tlen = jl_datatype_nfields(vt);
if (isnamedtuple) {
Expand Down
33 changes: 33 additions & 0 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,39 @@ end

@test static_shown(QuoteNode(:x)) == ":(:x)"

# PR #38049
@test static_shown(sum) == "Base.sum"
@test static_shown(+) == "Base.:(+)"
@test static_shown(typeof(+)) == "typeof(Base.:(+))"

struct var"#X#" end
var"#f#"() = 2
struct var"%X%" end # Invalid name without '#'

# (Just to make this test more sustainable,) we don't necesssarily need to test the exact
# output format, just ensure that it prints at least the parts we expect:
@test occursin(".var\"#X#\"", static_shown(var"#X#")) # Leading `.` tests it printed a module name.
@test occursin(r"Set{var\"[^\"]+\"} where var\"[^\"]+\"", static_shown(Set{<:Any}))

# Test that static_shown is returning valid, correct julia expressions
@testset "static_show() prints valid julia" begin
@testset for v in (
var"#X#",
var"#X#"(),
var"%X%",
var"%X%"(),
Vector,
Vector{<:Any},
Vector{var"#X#"},
+,
typeof(+),
var"#f#",
typeof(var"#f#"),
)
@test v == eval(Meta.parse(static_shown(v)))
end
end

# Test @show
let fname = tempname()
try
Expand Down

0 comments on commit 031c877

Please sign in to comment.