Skip to content
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

Implement conditional resources and modules #1014

Merged
merged 8 commits into from
Dec 14, 2020

Conversation

shenglol
Copy link
Contributor

@shenglol shenglol commented Nov 26, 2020

Added IfConditionSyntax to support conditional resources and modules. The things I'm planning to do in separate PRs:

  • Code completion for IfConditionSyntax
  • Decompiling conditional resources and nested deployments
  • Make conditional resources and modules nullable. Need people's feedback on this one. I created Provide a way to tag a resource as nullable #1015 to track it.

Closes #186.

@codecov-io
Copy link

codecov-io commented Nov 26, 2020

Codecov Report

Merging #1014 (18fd2d1) into main (f2f9111) will increase coverage by 0.52%.
The diff coverage is 88.99%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1014      +/-   ##
==========================================
+ Coverage   94.26%   94.78%   +0.52%     
==========================================
  Files         328      325       -3     
  Lines       15810    15772      -38     
  Branches       12        0      -12     
==========================================
+ Hits        14903    14950      +47     
+ Misses        907      822      -85     
Flag Coverage Δ
dotnet 94.78% <88.99%> (-0.04%) ⬇️
typescript ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/Bicep.Cli/Program.cs 90.09% <ø> (ø)
src/Bicep.Core/LanguageConstants.cs 98.86% <ø> (ø)
src/Bicep.Core/Semantics/ResourceSymbol.cs 100.00% <ø> (ø)
src/Bicep.LangServer/SemanticTokenVisitor.cs 0.00% <0.00%> (ø)
src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs 66.39% <12.50%> (-1.99%) ⬇️
src/Bicep.Core/Emit/ExpressionConverter.cs 96.08% <50.00%> (ø)
src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs 100.00% <100.00%> (ø)
src/Bicep.Core/Emit/EmitLimitationCalculator.cs 72.00% <100.00%> (ø)
src/Bicep.Core/Emit/TemplateWriter.cs 97.68% <100.00%> (+0.04%) ⬆️
src/Bicep.Core/Parsing/Parser.cs 94.67% <100.00%> (+0.32%) ⬆️
... and 14 more

@@ -223,9 +221,14 @@ private void EmitResource(ResourceSymbol resourceSymbol)

var typeReference = EmitHelpers.GetTypeReference(resourceSymbol);

if (resourceSymbol.DeclaringResource.Body is IfExpressionSyntax conditionalBody)
{
this.emitter.EmitProperty("condition", conditionalBody.ConditionExpression);
Copy link
Member

Choose a reason for hiding this comment

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

Btw, do we have any limitations on what can go into resource conditions in the runtime? Are all functions including stuff like reference() or listKeys() allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good questions. The documents didn't mention any limitations, but let me do some tests to figure out.

Copy link
Member

Choose a reason for hiding this comment

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

My expectation was deploy-time constants only (similar to name, type, apiVersion field requirements) because the engine calculates the deployment graph upfront. We should probably make a note to clarify this in the docs once you've tested this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Confirmed with template deployments that runtime functions are not allowed in a resource condition (also found this ARM unit test that validates it). I'll update the code to block the use of them, and I think @anthony-c-martin is right that we should update the docs to clarify this.

Copy link
Contributor Author

@shenglol shenglol Dec 9, 2020

Choose a reason for hiding this comment

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

reference() and list* functions are blocked now. We still need to block resource and module symbolic name references, which I will implement in a separate PR once Tarun's PR is merged.

src/Bicep.LangServer/SemanticTokenVisitor.cs Outdated Show resolved Hide resolved
src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs Outdated Show resolved Hide resolved
src/Bicep.Core/Emit/ExpressionConverter.cs Outdated Show resolved Hide resolved
Comment on lines 41 to 42
public SyntaxBase ResolvedBody =>
this.Body is IfExpressionSyntax conditionalBody ? conditionalBody.ConsequenceExpression : this.Body;
Copy link
Member

Choose a reason for hiding this comment

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

Does this require a grammar update? (grammar.md).

If I'm understanding correctly, looks like with these changes we've got something like:

resourcedecl -> "resource" identifier string "=" body
body -> conditionalBody | object
conditionalBody -> "if" "(" expression ")" object

Just wonder, rather than using recursion for the definition, would it make sense to model the grammar as:

resourcedecl -> "resource" identifier string = ifexpr? object
ifexpr -> "if" "(" expression ")"

This is similar to what we did for the param default syntax, and might make chaining (when we have loops) and recursion easier to deal with. I think it also removes the need for the ResolvedBody property.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think it makes sense to not use recursion for the definition, and I like how it simplifies things a lot. The recursion version may work better if we are going to add an alternative expression to support else, but I doubt that's necessary, since if...else... can be replaced with a ternary operator. I'm going to change the grammar to the suggested version.

Copy link
Contributor Author

@shenglol shenglol Dec 9, 2020

Choose a reason for hiding this comment

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

Updated the grammar to not use recursion. I changed IfExpressionSyntax to IfCondtionSyntax though, because it's not an expression anymore.

Comment on lines 868 to 869
var conditionExpression = this.WithRecovery(() => this.ParenthesizedExpression(true), RecoveryFlags.None, TokenType.LeftBrace, TokenType.NewLine);
var @object = this.WithRecovery(this.Object, GetSuppressionFlag(conditionExpression), TokenType.NewLine);
Copy link
Member

Choose a reason for hiding this comment

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

Mind adding a line to the invalid resources file for something like: ... if ({'a': b}.a == true) {? It's invalid syntax, just curious to see what sort of error message results from the recovery logic here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a test case. It produces a error message like expect a ) character... at the end, which is not very helpful. I also tried a invalid parenthesized expression in a resource declartion, and got a similar error message.
image

Copy link
Member

@anthony-c-martin anthony-c-martin left a comment

Choose a reason for hiding this comment

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

Looks great!

@shenglol shenglol merged commit 4b3e8b1 into main Dec 14, 2020
@shenglol shenglol deleted the shenglol/conditional-resources-and-modules branch December 14, 2020 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

implement resource conditions
5 participants