Skip to content

fix: support spaces in directive argument values#665

Open
rarguelloF wants to merge 8 commits intomainfrom
rarguellof/fix-directive-space-arg-values
Open

fix: support spaces in directive argument values#665
rarguelloF wants to merge 8 commits intomainfrom
rarguellof/fix-directive-space-arg-values

Conversation

@rarguelloF
Copy link
Copy Markdown
Contributor

@rarguelloF rarguelloF commented Jul 16, 2025

Adds support for spaces in directive argument values, either using single or double quotes, as in:

//dd:span span.name:'root handler' resource.name:"GET /"

This is useful since its pretty common to use spaces in span or resource names, and in span tag values in general.

@rarguelloF rarguelloF marked this pull request as ready for review July 16, 2025 15:29
@rarguelloF rarguelloF requested a review from a team as a code owner July 16, 2025 15:29
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test that demonstrates what happens in cases where the quoted string is broken (i.e, never closed / no space after close & before next / ...)? Those seem to be missing and they're the edge cases I worry about :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! added a few more test cases, please let me know if the behavior makes sense, or if there's any other test missing 🙏

@rarguelloF rarguelloF requested a review from RomainMuller July 24, 2025 07:04
Comment on lines +112 to +147
{
name: "unclosed double quote value",
prefix: "//dd:span",
comment: `//dd:span service.name:"my-service`,
want: []DirectiveArgument{
{Key: "service.name", Value: `"my-service`},
},
wantOk: true,
},
{
name: "unclosed single quote value",
prefix: "//dd:span",
comment: `//dd:span service.name:'my-service`,
want: []DirectiveArgument{
{Key: "service.name", Value: "'my-service"},
},
wantOk: true,
},
{
name: "missing space between quoted args",
prefix: "//dd:span",
comment: `//dd:span service.name:"my-service"resource.name:"GET /"`,
want: []DirectiveArgument{
{Key: "service.name", Value: `my-service"resource.name:"GET /`},
},
wantOk: true,
},
{
name: "quote starts but ends with different quote type",
prefix: "//dd:span",
comment: `//dd:span service.name:"my-service'`,
want: []DirectiveArgument{
{Key: "service.name", Value: `"my-service'`},
},
wantOk: true,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find those surprising -- I reckon it's almost guaranteed that the outcome is not what the user intended...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which one concretely? what do you think it should be the preferred behavior?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect a lot of the "improperly terminated" cases to result in an error or a warning being emitted.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rarguelloF Ensure that the value is a valid Go string literal using double quote, fail on anything else (unless it's an unquoted string without spaces).

@codecov
Copy link
Copy Markdown

codecov bot commented Nov 25, 2025

Codecov Report

❌ Patch coverage is 95.91837% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.89%. Comparing base (e061d12) to head (67796d3).
⚠️ Report is 42 commits behind head on main.

Files with missing lines Patch % Lines
...ernal/injector/aspect/advice/code/dot_directive.go 95.91% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #665      +/-   ##
==========================================
+ Coverage   65.72%   69.89%   +4.17%     
==========================================
  Files         113      116       +3     
  Lines        7926     6929     -997     
==========================================
- Hits         5209     4843     -366     
+ Misses       2192     1541     -651     
- Partials      525      545      +20     
Components Coverage Δ
Generators 83.23% <ø> (+2.98%) ⬆️
Instruments ∅ <ø> (∅)
Go Driver 77.08% <71.15%> (+1.27%) ⬆️
Toolexec Driver 74.73% <100.00%> (+7.20%) ⬆️
Aspects 77.34% <80.51%> (+5.43%) ⬆️
Injector 77.37% <80.81%> (+4.57%) ⬆️
Job Server 68.38% <55.55%> (+2.46%) ⬆️
Other 69.89% <68.56%> (+4.17%) ⬆️
Files with missing lines Coverage Δ
...ernal/injector/aspect/advice/code/dot_directive.go 96.15% <95.91%> (+31.44%) ⬆️

... and 107 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

y1yang0 added a commit to open-telemetry/opentelemetry-go-compile-instrumentation that referenced this pull request Mar 25, 2026
#375)

## Description

Adds a new **Directive Rule** — a join point that matches AST nodes
annotated with magic comments (e.g., `//dd:span span.name:"my
operation"`). This is a **pure filter** with no advice: it matches
during the setup phase but performs no AST transformation during
instrumentation. It becomes useful when `all-of` combinators land,
allowing directive annotations to be combined with other join points.

### What's included

- **New rule type: `directive`** — matches source files containing a
`//directive` comment. The directive name is specified without the `//`
prefix. Validation rejects empty directives, directives with spaces, and
directives that start with `//`.

- **Directive matching** (`matchDirective`) — validates that the comment
is a line comment (not block), has no space after `//`, matches the full
directive name (not a prefix), and requires whitespace after the name if
arguments follow. Ports the matching semantics from Orchestrion's
`directive.matches`.

- **Argument parsing** (`ParseDirectiveArgs`, `scanArgs`, `tokenize`) —
parses `key:value` arguments from directive comments. Values may be Go
double-quoted strings (handled via `strconv.Unquote`). Single quotes and
malformed input are rejected. This infrastructure is ready for use when
combinators wire directive args into the instrument phase.

- **Setup integration** — `createRuleFromFields` dispatches on the
`directive` YAML key. `preciseMatching` uses a lazy-loaded `os.ReadFile`
+ `bytes.Contains` pre-filter (since `ParseFileFast` skips comments, the
raw bytes are needed).

- **Instrument phase** — directive rules are intentionally **excluded
from `groupRules`** to avoid a wasted parse+write cycle for files that
only have directive rules (no AST transformation to apply).

- **Tests** — unit tests for matching (10 cases), arg parsing (9 cases),
directive args (3 cases), rule validation (5 cases), rule factory (2
cases), `TestGroupRules` exclusion, and a golden integration test.

### Rule format

```yaml
my_directive_rule:
  target: main
  directive: "otelc:span"
```

### Rule fields

| Field       | Type   | Required | Description |
|-------------|--------|----------|-------------|
| `directive` | string | yes | Directive name to match (without `//`) |

### Design decisions

- **Pure filter, no advice** — no `before`/`after`/`assign_value`
fields. The rule only participates in setup-phase matching.
- **`bytes.Contains` pre-filter** — a loose but fast check on raw file
bytes. False positives (e.g., the directive appearing inside a string
literal) are acceptable because the precise `matchDirective` function
will be used for real matching when combinators land.
- **Excluded from `groupRules`** — since `applyDirectiveRule` would be a
no-op, directive rules don't enter the instrument phase's
file-processing loop, avoiding unnecessary AST parse+write cycles.

## Motivation

Implements the Orchestrion-equivalent `directive` join point for
compile-time instrumentation. This is a prerequisite for the `all-of`
combinator feature, which will allow rules like "instrument functions
annotated with `//dd:span`" by combining directive matching with
function join points.

Design follows feedback on Orchestrion PR
DataDog/orchestrion#665 regarding Go
double-quoted string support for directive argument values.

---

## Checklist

- [x] PR title follows [conventional
commits](https://www.conventionalcommits.org/) format
- [x] Code formatted: `make format`
- [x] Linters pass: `make lint`
- [x] Tests pass: `make test`
- [x] Tests added for new functionality
- [x] Documentation updated (if applicable)

---------

Co-authored-by: Yi Yang <qingfeng.yy@alibaba-inc.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants