- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 187
 
Detect undefined variables #844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| 
           @sebastienros Hi, could you please approve it? This feature is really required,  | 
    
| 
           I am wondering if instead of checking in  That would improve two things: 
 Other concerns: 
 One more thing actually, what if the context had an event handler like we already have with  If you don't see any major issue with these suggestions: event + delegate buffering to user then I think that should be the way to go. Less responsibility, and potentially more flexibility.  | 
    
| 
           @sebastienros Please take a look to the second commit. Did I get your idea right?  | 
    
| 
           @rodion-m I updated your PR to my preferences, I hope you won't mind. It's simpler, all your tests are passing (though not using the exception directly) and added a few more. The main idea is to reuse   | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for handling undefined variables in Fluid templates by introducing an Undefined delegate that can be configured on TemplateOptions and TemplateContext. This allows developers to track missing variables during rendering, provide custom fallback values, or log undefined accesses for debugging purposes.
- Adds 
UndefinedDelegatetoTemplateOptionsandTemplateContextfor tracking undefined variable access - Updates 
ObjectValueBaseandMemberExpressionto invoke theUndefineddelegate when variables cannot be resolved - Refactors code in 
ObjectValueBaseto use switch expressions and removes unnecessary comments - Includes comprehensive documentation in README with three usage examples
 - Adds 37 test cases covering various scenarios of undefined variable handling
 
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description | 
|---|---|
| README.md | Adds comprehensive documentation section explaining the Undefined delegate feature with three practical examples | 
| Fluid/TemplateOptions.cs | Introduces UndefinedDelegate type and Undefined property, reorders using statements | 
| Fluid/TemplateContext.cs | Adds Undefined property and initializes it from options, changes NilValue to EmptyValue for null values | 
| Fluid/Values/ObjectValueBase.cs | Updates member access logic to invoke Undefined delegate, refactors to switch expressions, reorders using statements | 
| Fluid/Ast/MemberExpression.cs | Adds Undefined delegate invocation for top-level undefined variables | 
| Fluid/Scope.cs | Reorders using statements | 
| Fluid/Parser/FluidTemplate.cs | Reorders using statements | 
| Fluid.Tests/StrictVariableTests.cs | Adds comprehensive test suite with 37 test cases validating undefined variable handling | 
Comments suppressed due to low confidence (1)
Fluid/Values/ObjectValueBase.cs:120
- When 
targetbecomes null during nested property traversal andcontext.Undefinedis set, the delegate is never invoked. The method should callcontext.Undefined.Invoke(string.Join(\".\", segments))before returningNilValue.Instanceto track this undefined access. 
                if (target == null)
                {
                    return NilValue.Instance;
                }
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| 
           After chatting with Copilot I re-introduced   | 
    
| 
           Hi @sebastienros ! Thank you for the improvements. I just didn't get why did we miss   | 
    
| 
           It's about flexibility. Users won't have to catch an exception, unless thy want to (like you seem to), they can also ignore specific missing variables, or just decide to log them and still get the template running fine, and there is no buffering involved with any of these options. In your case, here is the code you will have to to write to create exception: var context = new TemplateContext();
context.Undefined = name => throw new StrictVariableException($"Undefined variable: {name}");
await template.RenderAsync(text, context);
}It's not much more work, but users have more flexibility. You can decide to put the code in the TemplateOptions and reuse it too if you just want to throw as soon as one is not found. Or use a   | 
    
| 
           Yeah, I got it. Anyway, what do you think if we add an option like ThrowOnUndefinedVariable that will just init Undefined delegate like you showed? I really believe that it's important to have such an option from a UX perspective. If a user set both these options - we can throw an exception. Or an alternative: Just add a descendant StrictTemplateOptions without Undefined delegate all (it should be preset). This solution will be more type safe. @sebastienros thoughts?  | 
    
StrictVariables option
Fixes #64
Fixes #562
Key Features Implemented
✅ Collection Strategy: Collects ALL missing variables before throwing exception
✅ No Partial Output: Uses buffering to prevent partial renders on error
✅ Backward Compatible: StrictVariables defaults to false
✅ Deduplication: HashSet prevents duplicate missing variables
✅ Comprehensive: Tracks missing variables in all contexts (scopes, properties, nested access)
✅ Clear Error Messages: Exception message lists all missing variables
✅ Reusability: Clears tracking between renders for context reuse
Testing Coverage
The test suite includes 30+ tests covering:
Default behavior verification
Simple and complex variable access patterns
Property access on objects
Nested property access
Control structures (if, for, case)
Filters and assignments
Model fallback scenarios
Edge cases and error conditions