[DTrace-devel] [PATCH] parser: validate scoped external names
Alan Maguire
alan.maguire at oracle.com
Fri Sep 11 14:32:12 UTC 2026
On 28/08/2026 19:48, Kris Van Hees via DTrace-devel wrote:
> Centralize parsing of D scoping markers used by external type and symbol
> references. The helper now returns the resolved scope and identifier
> pieces, supports kernel and userspace markers for both type and symbol
> lookup, and rejects empty operands, runs longer than two backticks, and
> repeated scoping markers as D_SYNTAX instead of falling through to lookup
> failures.
>
> Recognize dotted scoped identifiers as a single lexer token so module
> names like foo.bar resolve as one external symbol reference.
>
> Add lexer and type regression tests for malformed scoped identifiers,
> dotted scopes, empty scope operands, scope-kind mismatches, and scoping
> operators in declarations. Update the sizeof unknown-symbol test to use
> a non-empty symbol name.
>
> Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
One AI-assisted issue found here I think; D allows us to scope type references IIRC; i.e.
struct vmlinux`task_struct *task;
or sizeof(struct vmlinux`task_struct)
However the backtick support here also allows a backtick in local type declarations
which doesn't make sense:
struct vmlinux`task_struct {
int replacement;
};
We can likely distinguish type declaration (no ` allowed) from scoped variable
declaration in dt_decl_sou()/dt_decl_enum() but I think we need to do so.
> ---
> libdtrace/dt_decl.c | 6 +-
> libdtrace/dt_lex.l | 5 +
> libdtrace/dt_parser.c | 140 ++++++++++++------
> libdtrace/dt_parser.h | 1 +
> .../err.D_IDENT_UNDEF.scope-dot-kernel.d | 19 +++
> .../err.D_IDENT_UNDEF.scope-dot-kernel.r | 2 +
> .../lexer/err.D_IDENT_UNDEF.scope-dot.d | 19 +++
> .../lexer/err.D_IDENT_UNDEF.scope-dot.r | 2 +
> .../err.D_SYM_BADREF.scope-kernel-as-user.d | 18 +++
> .../err.D_SYM_BADREF.scope-kernel-as-user.r | 2 +
> .../err.D_SYM_BADREF.scope-user-as-kernel.d | 20 +++
> .../err.D_SYM_BADREF.scope-user-as-kernel.r | 2 +
> .../err.D_SYNTAX.scope-empty-bare-user.d | 19 +++
> .../err.D_SYNTAX.scope-empty-bare-user.r | 2 +
> .../lexer/err.D_SYNTAX.scope-empty-bare.d | 19 +++
> .../lexer/err.D_SYNTAX.scope-empty-bare.r | 2 +
> .../lexer/err.D_SYNTAX.scope-empty-dot-user.d | 19 +++
> .../lexer/err.D_SYNTAX.scope-empty-dot-user.r | 2 +
> .../lexer/err.D_SYNTAX.scope-empty-user.d | 19 +++
> .../lexer/err.D_SYNTAX.scope-empty-user.r | 2 +
> .../unittest/lexer/err.D_SYNTAX.scope-empty.d | 18 +++
> .../unittest/lexer/err.D_SYNTAX.scope-empty.r | 2 +
> .../lexer/err.D_SYNTAX.scope-multiple.d | 19 +++
> .../lexer/err.D_SYNTAX.scope-multiple.r | 2 +
> .../lexer/err.D_SYNTAX.scope-triple.d | 19 +++
> .../lexer/err.D_SYNTAX.scope-triple.r | 2 +
> test/unittest/lexer/tst.scope-user-explicit.d | 22 +++
> .../sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d | 2 +-
> .../sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r | 2 +-
> .../err.D_DECL_SCOPE.scopeop-enumerator.d | 16 ++
> .../err.D_DECL_SCOPE.scopeop-enumerator.r | 2 +
> .../types/err.D_DECL_SCOPE.scopeop-ident.d | 19 +++
> .../types/err.D_DECL_SCOPE.scopeop-ident.r | 2 +
> .../types/err.D_DECL_SCOPE.scopeop-member.d | 16 ++
> .../types/err.D_DECL_SCOPE.scopeop-member.r | 2 +
> 35 files changed, 414 insertions(+), 51 deletions(-)
> create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d
> create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r
> create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d
> create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r
> create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d
> create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r
> create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d
> create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-multiple.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-multiple.r
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-triple.d
> create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-triple.r
> create mode 100644 test/unittest/lexer/tst.scope-user-explicit.d
> create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d
> create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r
> create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d
> create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r
> create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d
> create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r
>
> diff --git a/libdtrace/dt_decl.c b/libdtrace/dt_decl.c
> index 1f4936dd..68999422 100644
> --- a/libdtrace/dt_decl.c
> +++ b/libdtrace/dt_decl.c
> @@ -233,10 +233,8 @@ dt_decl_spec(ushort_t kind, char *name)
> ddp->dd_kind = kind;
> ddp->dd_name = name;
>
> - if (name != NULL && strchr(name, '`') != NULL) {
> - xyerror(D_DECL_SCOPE, "D scoping operator may not be used "
> - "in a type name\n");
> - }
> + if (name != NULL && dt_scope_parse(name, NULL, NULL) < 0)
> + xyerror(D_SYNTAX, "syntax error near \"%s\"\n", name);
>
> return dt_decl_check(ddp);
> }
> diff --git a/libdtrace/dt_lex.l b/libdtrace/dt_lex.l
> index fd70aa0a..bf133676 100644
> --- a/libdtrace/dt_lex.l
> +++ b/libdtrace/dt_lex.l
> @@ -50,6 +50,7 @@ static size_t dt_input(char *buf, size_t max_size);
> RGX_AGG "@"[a-zA-Z_][0-9a-zA-Z_]*
> RGX_PSPEC [-$:a-zA-Z_.?*\\\[\]!][-$:0-9a-zA-Z_.`?*\\\[\]!]*
> RGX_IDENT ([a-zA-Z_`][0-9a-zA-Z_`]*)|([0-9][0-9a-zA-Z_]*`[0-9a-zA-Z_`]*)
> +RGX_SCOPE_IDENT [0-9a-zA-Z_][0-9a-zA-Z_]*(\.[0-9a-zA-Z_]+)+``?[0-9a-zA-Z_]+
> RGX_INT ([0-9]+|0[xX][0-9A-Fa-f]+)[uU]?[lL]?[lL]?
> RGX_FP ([0-9]+("."?)[0-9]*|"."[0-9]+)((e|E)("+"|-)?[0-9]+)?[fFlL]?
> RGX_WS [\f\n\r\t\v ]
> @@ -299,6 +300,7 @@ if (yypcb->pcb_token != 0) {
> return DT_TOK_INT;
> }
>
> +<S0>{RGX_SCOPE_IDENT} |
> <S0,SIDENT>{RGX_IDENT} {
> return id_or_type(yytext);
> }
> @@ -728,6 +730,9 @@ id_or_type(const char *s)
> int c0, c1;
> dt_ident_t *idp;
>
> + if (dt_scope_parse(s, NULL, NULL) < 0)
> + xyerror(D_SYNTAX, "syntax error near \"%s\"\n", s);
> +
> if ((s = yylval.l_str = strdup(s)) == NULL)
> longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
>
> diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c
> index e71c0985..1fd0f5b4 100644
> --- a/libdtrace/dt_parser.c
> +++ b/libdtrace/dt_parser.c
> @@ -168,45 +168,96 @@ opstr(int op)
> }
> }
>
> +/*
> + * Parse the D scoping mark used in external type and symbol names. A scoped
> + * name can contain one mark only: ` for kernel scope, or `` for user scope.
> + * Longer runs and repeated marks are malformed. Return the scope kind as 0
> + * for no scope, 1 for kernel scope, or 2 for user scope.
> + */
> int
> -dt_type_lookup(const char *s, dtrace_typeinfo_t *tip)
> +dt_scope_parse(const char *s, const char **scopep, const char **identp)
> {
> - static const char delimiters[] = " \t\n\r\v\f*`";
> - dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> - const char *p, *q, *end, *obj;
> + static const char delimiters[] = " \t\n\r\v\f*";
> + const char *mark = strchr(s, '`');
> + const char *scope;
> + size_t marklen = 1;
> +
> + if (mark == NULL) {
> + if (scopep != NULL)
> + *scopep = DTRACE_OBJ_EXEC;
> + if (identp != NULL)
> + *identp = s;
> + return 0;
> + }
>
> - for (p = s, end = s + strlen(s); *p != '\0'; p = q) {
> - while (isspace(*p))
> - p++; /* skip leading whitespace prior to token */
> + if (mark[1] == '`') {
> + marklen = 2;
> + if (mark[2] == '`')
> + return -1;
> + }
>
> - if (p == end || (q = strpbrk(p + 1, delimiters)) == NULL)
> - break; /* empty string or single token remaining */
> + if (mark[marklen] == '\0')
> + return -1;
>
> - if (*q == '`') {
> - char *object = alloca((size_t)(q - p) + 1);
> - char *type = alloca((size_t)(end - s) + 1);
> + if (strchr(mark + marklen, '`') != NULL)
> + return -1;
>
> - /*
> - * Copy from the start of the token (p) to the location
> - * backquote (q) to extract the nul-terminated object.
> - */
> - memcpy(object, p, (size_t)(q - p));
> - object[(size_t)(q - p)] = '\0';
> + for (scope = mark;
> + scope > s && strchr(delimiters, scope[-1]) == NULL; )
> + scope--;
>
> - /*
> - * Copy the original string up to the start of this
> - * token (p) into type, and then concatenate everything
> - * after q. This is the type name without the object.
> - */
> - memcpy(type, s, (size_t)(p - s));
> - memcpy(type + (size_t)(p - s), q + 1,
> - strlen(q + 1) + 1);
> + if (scope == mark)
> + scope = marklen == 2 ? DTRACE_OBJ_UMODS : DTRACE_OBJ_KMODS;
> +
> + if (scopep != NULL)
> + *scopep = scope;
> + if (identp != NULL)
> + *identp = mark + marklen;
> +
> + return marklen;
> +}
> +
> +int
> +dt_type_lookup(const char *s, dtrace_typeinfo_t *tip)
> +{
> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> + const char *scope, *ident, *start, *obj;
> + int scopekind;
> +
> + scopekind = dt_scope_parse(s, &scope, &ident);
> + if (scopekind < 0)
> + return dt_set_errno(dtp, EDT_BADSCOPE);
>
> - if (strchr(q + 1, '`') != NULL)
> - return dt_set_errno(dtp, EDT_BADSCOPE);
> + if (scopekind > 0) {
> + char *type;
> +
> + /*
> + * If there is no explicit object before the mark, search all
> + * kernel or user objects depending on the mark width.
> + */
> + if (scope == DTRACE_OBJ_KMODS || scope == DTRACE_OBJ_UMODS) {
> + start = ident - scopekind;
> + obj = scope;
> + } else {
> + size_t objlen = (size_t)(ident - scope) - scopekind;
> + char *object = alloca(objlen + 1);
>
> - return dtrace_lookup_by_type(dtp, object, type, tip);
> + memcpy(object, scope, objlen);
> + object[objlen] = '\0';
> + start = scope;
> + obj = object;
> }
> +
> + /*
> + * Copy the original string up to the start of this token
> + * into type, and then concatenate everything after the scoping
> + * mark. This is the type name without the object and mark.
> + */
> + type = alloca((size_t)(start - s) + strlen(ident) + 1);
> + memcpy(type, s, (size_t)(start - s));
> + memcpy(type + (size_t)(start - s), ident, strlen(ident) + 1);
> +
> + return dtrace_lookup_by_type(dtp, obj, type, tip);
> }
>
> if (yypcb->pcb_idepth != 0)
> @@ -2767,9 +2818,10 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
> dtrace_syminfo_t dts;
> GElf_Sym sym;
>
> - const char *scope, *mark;
> + const char *scope, *ident, *markstr;
> uchar_t dnkind;
> char *name;
> + int scopekind;
>
> /*
> * Look for scoping marks in the identifier. If one is found, set our
> @@ -2779,18 +2831,16 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
> * Otherwise we set scope to DTRACE_OBJ_EXEC, indicating that normal
> * scope is desired and we should search the specified idhash.
> */
> - if ((name = strrchr(dnp->dn_string, '`')) != NULL) {
> - if (name > dnp->dn_string && name[-1] == '`') {
> - uref++;
> - name[-1] = '\0';
> - }
> -
> - if (name == dnp->dn_string + uref)
> - scope = uref ? DTRACE_OBJ_UMODS : DTRACE_OBJ_KMODS;
> - else
> - scope = dnp->dn_string;
> + scopekind = dt_scope_parse(dnp->dn_string, &scope, &ident);
> + if (scopekind < 0) {
> + xyerror(D_SYNTAX, "syntax error near \"%s\"\n",
> + dnp->dn_string);
> + }
>
> - *name++ = '\0'; /* leave name pointing after scoping mark */
> + if (scopekind > 0) {
> + uref = scopekind == 2;
> + name = (char *)ident;
> + *(char *)(ident - scopekind) = '\0';
> dnkind = DT_NODE_VAR;
>
> } else if (idkind == DT_IDENT_AGG) {
> @@ -2810,7 +2860,7 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
> * errno appropriately and that error will be reported instead.
> */
> dt_set_errno(dtp, EDT_NOVAR);
> - mark = uref ? "``" : "`";
> + markstr = uref ? "``" : "`";
>
> if (scope == DTRACE_OBJ_EXEC && (
> (dhp != dtp->dt_globals &&
> @@ -2902,13 +2952,13 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
> xyerror(D_SYM_MODEL, "cannot use %s symbol "
> "%s%s%s in a %s D program\n",
> dt_module_modelname(mp), dts.object,
> - mark, dts.name,
> + markstr, dts.name,
> dt_module_modelname(dtp->dt_ddefs));
> }
>
> xyerror(D_SYM_NOTYPES, "no symbolic type information "
> "is available for %s%s%s: %s\n",
> - dts.object, mark, dts.name,
> + dts.object, markstr, dts.name,
> dtrace_errmsg(dtp, dtrace_errno(dtp)));
> }
>
> @@ -3014,7 +3064,7 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
>
> } else if (scope != DTRACE_OBJ_EXEC) {
> xyerror(D_IDENT_UNDEF, "failed to resolve %s%s%s: %s\n",
> - dnp->dn_string, mark, name,
> + dnp->dn_string, markstr, name,
> dtrace_errmsg(dtp, dtrace_errno(dtp)));
> } else {
> xyerror(D_IDENT_UNDEF, "failed to resolve %s: %s\n",
> diff --git a/libdtrace/dt_parser.h b/libdtrace/dt_parser.h
> index d74d658a..b9149220 100644
> --- a/libdtrace/dt_parser.h
> +++ b/libdtrace/dt_parser.h
> @@ -211,6 +211,7 @@ extern dt_node_t *dt_node_tstring(dt_node_t *, uintmax_t);
> extern dt_node_t *dt_node_link(dt_node_t *, dt_node_t *);
> extern dt_node_t *dt_node_cook(dt_node_t *, uint_t);
> extern void dt_cook_taint_alloca(dt_node_t *, dt_ident_t *, dt_node_t *);
> +extern int dt_scope_parse(const char *, const char **, const char **);
>
> extern dt_node_t *dt_node_xalloc(dtrace_hdl_t *, int);
> extern void dt_node_free(dt_node_t *);
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d
> new file mode 100644
> index 00000000..2a267567
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Dotted kernel module scopes lex as a single external symbol
> + * reference.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(foo.bar`baz);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r
> new file mode 100644
> index 00000000..89a9f9c5
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d: [D_IDENT_UNDEF] line 17: failed to resolve foo.bar`baz: Module is no longer loaded
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d
> new file mode 100644
> index 00000000..6b3dcfda
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Dotted userspace module scopes lex as a single external symbol
> + * reference.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(foo.bar``baz);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r
> new file mode 100644
> index 00000000..7734e29f
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d: [D_IDENT_UNDEF] line 17: failed to resolve foo.bar``baz: Module is no longer loaded
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d
> new file mode 100644
> index 00000000..d332191b
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d
> @@ -0,0 +1,18 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Kernel symbols cannot be referenced using userspace scoping.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(vmlinux``major_names);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r
> new file mode 100644
> index 00000000..718b2b31
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d: [D_SYM_BADREF] line 16: kernel module 'vmlinux' symbol 'major_names' may not be referenced as a user symbol
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d
> new file mode 100644
> index 00000000..bc7e8b9f
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d
> @@ -0,0 +1,20 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/* @@trigger: testprobe */
> +
> +/*
> + * ASSERTION: Userspace symbols cannot be referenced using kernel scoping.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(&testprobe`main);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r
> new file mode 100644
> index 00000000..87169597
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d: [D_SYM_BADREF] line 18: user module 'testprobe' symbol 'main' may not be referenced as a kernel symbol
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d
> new file mode 100644
> index 00000000..d7cff89f
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A bare userspace D scoping operator with an empty identifier is a
> + * syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(``);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r
> new file mode 100644
> index 00000000..8e9cd1ff
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d: [D_SYNTAX] line 17: syntax error near "``"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d
> new file mode 100644
> index 00000000..44b4f60e
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A bare D scoping operator with an empty identifier is a syntax
> + * error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(`);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r
> new file mode 100644
> index 00000000..bbba5fe8
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d: [D_SYNTAX] line 17: syntax error near "`"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d
> new file mode 100644
> index 00000000..5ead7b3e
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A dotted userspace D scoping operator with an empty identifier is
> + * a syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(foo.bar``);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r
> new file mode 100644
> index 00000000..b07c07b1
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d: [D_SYNTAX] line 17: syntax error near "bar``"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d
> new file mode 100644
> index 00000000..d3a927d7
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A userspace D scoping operator with an empty identifier is a
> + * syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(bar``);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r
> new file mode 100644
> index 00000000..dbf319ea
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d: [D_SYNTAX] line 17: syntax error near "bar``"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty.d
> new file mode 100644
> index 00000000..e32b3812
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty.d
> @@ -0,0 +1,18 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A D scoping operator with an empty identifier is a syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(foo`);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty.r
> new file mode 100644
> index 00000000..d78728b2
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty.d: [D_SYNTAX] line 16: syntax error near "foo`"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-multiple.d b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.d
> new file mode 100644
> index 00000000..5e236c45
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Multiple D scoping operators in one identifier are a syntax
> + * error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(foo``bar``baz);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-multiple.r b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.r
> new file mode 100644
> index 00000000..cefc56ad
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-multiple.d: [D_SYNTAX] line 17: syntax error near "foo``bar``baz"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-triple.d b/test/unittest/lexer/err.D_SYNTAX.scope-triple.d
> new file mode 100644
> index 00000000..41f7391c
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-triple.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: More than two consecutive D scoping operator characters are a
> + * syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(foo```bar);
> + exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-triple.r b/test/unittest/lexer/err.D_SYNTAX.scope-triple.r
> new file mode 100644
> index 00000000..4474c502
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-triple.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-triple.d: [D_SYNTAX] line 17: syntax error near "foo```bar"
> diff --git a/test/unittest/lexer/tst.scope-user-explicit.d b/test/unittest/lexer/tst.scope-user-explicit.d
> new file mode 100644
> index 00000000..2d649f6f
> --- /dev/null
> +++ b/test/unittest/lexer/tst.scope-user-explicit.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/* @@trigger: testprobe */
> +/* @@runtest-opts: -e */
> +
> +/*
> + * ASSERTION: Explicit userspace module scoping resolves as a userspace
> + * external symbol reference.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> + trace(&testprobe``main);
> + exit(0);
> +}
> diff --git a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d
> index 56c93d43..4de9322d 100644
> --- a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d
> +++ b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d
> @@ -16,6 +16,6 @@
>
> BEGIN
> {
> - printf("sizeof(`): %d\n", sizeof(`));
> + printf("sizeof(`unknown_symbol): %d\n", sizeof(`unknown_symbol));
> exit(0);
> }
> diff --git a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r
> index 462469b7..0f46ad1e 100644
> --- a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r
> +++ b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r
> @@ -1,2 +1,2 @@
> -- @@stderr --
> -dtrace: failed to compile script test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d: [D_IDENT_UNDEF] line 19: failed to resolve `: Unknown symbol name
> +dtrace: failed to compile script test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d: [D_IDENT_UNDEF] line 19: failed to resolve `unknown_symbol: Unknown symbol name
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d
> new file mode 100644
> index 00000000..51068c20
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d
> @@ -0,0 +1,16 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: D scoping operators are not permitted in enumerator names.
> + */
> +
> +#pragma D option quiet
> +
> +enum scopeop_enum {
> + foo`bar
> +};
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r
> new file mode 100644
> index 00000000..06ef912b
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d: [D_DECL_SCOPE] line 15: D scoping operator may not be used in an enumerator name (foo`bar)
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d
> new file mode 100644
> index 00000000..6042e208
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: D scoping operators are not permitted in declaration names.
> + */
> +
> +#pragma D option quiet
> +
> +int foo`bar;
> +
> +BEGIN
> +{
> + exit(1);
> +}
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r
> new file mode 100644
> index 00000000..7b2e6797
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d: [D_DECL_SCOPE] line 14: D scoping operator may not be used in a declaration name (foo`bar)
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d
> new file mode 100644
> index 00000000..5fe36a00
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d
> @@ -0,0 +1,16 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: D scoping operators are not permitted in struct member names.
> + */
> +
> +#pragma D option quiet
> +
> +struct scopeop_struct {
> + int foo`bar;
> +};
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r
> new file mode 100644
> index 00000000..978ef79d
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d: [D_DECL_SCOPE] line 15: D scoping operator may not be used in a member name (foo`bar)
> --
> 2.52.0
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
>
More information about the DTrace-devel
mailing list