fix: detect ref() calls nested in Jinja macro arguments - #92
Merged
Conversation
When a model calls a macro that minijinja cannot resolve (e.g. a package
macro like shared_macros.import_cte or dbt_utils.star), rendering fails
and extraction fell back to a regex that only matched bare
{{ ref('...') }} expressions, dropping any ref()/source() nested in
macro arguments.
- Keep refs/sources recorded before the render failure instead of
discarding the partial extraction (minijinja evaluates call arguments
before resolving the callee, so they are valid)
- Relax the regex fallback to find ref()/source() anywhere inside
{{ ... }} and {% ... %} blocks, excluding {# #} comments and
{% raw %} sections, and merge it with the partial jinja result
This also fixes refs inside {% set %} statements being missed by the
regex fallback.
Fixes #91
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The block-scoped regex fallback scanned raw jinja block text, so
ref()/source() appearing inside a string literal (e.g. a log message
like {{ log("see ref('x')") }}) would create a phantom dependency.
Track quoted string literal spans per block, honoring backslash
escapes, and skip matches that start inside one.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ref() calls nested in Jinja macro arguments
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes incomplete dbt lineage extraction when ref()/source() calls appear inside macro arguments (especially namespaced/package macros that minijinja can’t resolve). It does this by preserving partial Jinja extraction results on render failure and enhancing the regex fallback to detect calls anywhere inside Jinja blocks.
Changes:
- Preserve refs/sources/config recorded during a failed minijinja render and merge with regex results instead of discarding partial extraction.
- Update regex fallback to scan only inside
{{ ... }}/{% ... %}blocks (excluding{# #}and{% raw %}) and skip matches inside string literals. - Add unit tests covering nested macro args,
{% set %}statements, raw blocks, and string-literal false-positive guards.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| crates/dlin-core/src/parser/sql.rs | Improves regex fallback extraction (block-scoped scanning, raw/comment stripping, string-literal guarding) and merges regex results with partial Jinja extraction when rendering fails. |
| crates/dlin-core/src/parser/jinja.rs | Changes Jinja extraction to return a structured outcome (partial extraction + completion flag) and makes merge logic reusable for combining partial + fallback results. |
Comments suppressed due to low confidence (1)
crates/dlin-core/src/parser/sql.rs:320
extract_config_regexstrips only{# ... #}comments, but not{% raw %}...{% endraw %}blocks. Since{% raw %}content is not evaluated by Jinja, a{{ config(...) }}inside a raw block would be incorrectly treated as real config whenextract_allfalls back to regex merging on render failures. Using the same inert-Jinja stripping as ref/source extraction avoids this false positive.
extract_all(sql, macro_prefix).config
}
/// Regex fallback for extracting config() settings
fn extract_config_regex(sql: &str) -> SqlConfig {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #91
Problem
ref()calls passed as arguments to macros that minijinja cannot resolve (package macros such asshared_macros.import_cte(...)ordbt_utils.star(from=ref(...))) were not detected, producing incomplete lineage.Two layers both dropped them:
ref()as a whole{{ ref('...') }}expression, so calls nested in macro arguments or{% set %}statements never matched.Fix
{{ unknown_macro(ref('a')) }}recordsref('a')even though rendering fails. This also recovers dynamically built refs likeref('model_' ~ var('env'))that no regex can find.ref()/source()anywhere inside{{ ... }}and{% ... %}blocks, then merge it with the partial jinja result (deduplicated).{# #}comments and{% raw %}sections are excluded, text outside jinja blocks (e.g. SQL comments) is not scanned, and matches starting inside string literals are skipped so{{ log("see ref('x')") }}does not create a phantom edge.Verification
{% set %}, refs after the failure point, dynamic refs, string-literal and{% raw %}false-positive guardsimport_ctepattern from the issue now shows all three upstream models indlin graph🤖 Generated with Claude Code