Skip to content

Commit 93045ea

Browse files
authored
Don't crash when nonexistent symbol is used with external inputs (#19260)
## Description Below code was crashing language server due to unhandled exception when analyzing external input functions. Updated `InlineParameterRewriter` to explicity throw `ExpressionException` so it can be caught and handled gracefully during external input analysis. ```bicep import { myVar, helperFunction } from 'main.bicep' // imported symbols don't exist in 'main.bicep' param p12 = '${myVar}-${helperFunction()}-${externalInput('custom')}' ``` ## Checklist - [x] I have read and adhere to the [contribution guide](https://github.com/Azure/bicep/blob/main/CONTRIBUTING.md). ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/19260)
1 parent b63f44f commit 93045ea

File tree

10 files changed

+173
-23
lines changed

10 files changed

+173
-23
lines changed

src/Bicep.Core.IntegrationTests/ParameterFileTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,31 @@ public void ExternalInput_parameter_with_non_external_input_variable_references_
367367
});
368368
}
369369

370+
[TestMethod]
371+
public void ExternalInput_parameter_with_non_existent_symbol_reference_should_return_diagnostics()
372+
{
373+
var result = CompilationHelper.CompileParams(
374+
("main.bicep", @"
375+
"),
376+
("parameters.bicepparam", @"
377+
using none
378+
import { foo } from 'main.bicep' // foo doesn't exist in main.bicep
379+
param bar = '${foo}-${externalInput('test')}'
380+
var baz = foo('test')
381+
param qux = externalInput('kind', baz)
382+
"));
383+
384+
result.Should().HaveDiagnostics(
385+
[
386+
("BCP360", DiagnosticLevel.Error, "The 'foo' symbol was not found in (or was not exported by) the imported template."),
387+
("BCP063", DiagnosticLevel.Error, "The name \"foo\" is not a parameter, variable, resource or module."),
388+
("BCP059", DiagnosticLevel.Error, "The name \"foo\" is not a function."),
389+
("BCP338", DiagnosticLevel.Error, "Failed to evaluate function \"externalInput('kind', baz)\": Failed to evaluate variable \"baz\": The template function 'foo' is not valid. Please see https://aka.ms/arm-functions for usage details."),
390+
("BCP062", DiagnosticLevel.Error, "The referenced declaration with name \"baz\" is not valid."),
391+
]);
392+
393+
}
394+
370395
[TestMethod]
371396
public void ExternalInput_parameter_with_unevaluable_imported_variable_references_returns_diagnostic()
372397
{

src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_ExternalInputs/parameters.bicepparam

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
using none
22

3+
import { exportedVariable, helperFunction } from 'main.bicep'
4+
5+
param p12 = '${exportedVariable}-${externalInput('custom', helperFunction())}'
6+
37
var myVar = 1 + 2
48
param p = externalInput('sys.envVar', myVar)
59

src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_ExternalInputs/parameters.diagnostics.bicepparam

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
using none
22

3+
import { exportedVariable, helperFunction } from 'main.bicep'
4+
//@[09:25) [BCP360 (Error)] The 'exportedVariable' symbol was not found in (or was not exported by) the imported template. (bicep https://aka.ms/bicep/core-diagnostics#BCP360) |exportedVariable|
5+
//@[27:41) [BCP360 (Error)] The 'helperFunction' symbol was not found in (or was not exported by) the imported template. (bicep https://aka.ms/bicep/core-diagnostics#BCP360) |helperFunction|
6+
7+
param p12 = '${exportedVariable}-${externalInput('custom', helperFunction())}'
8+
//@[15:31) [BCP063 (Error)] The name "exportedVariable" is not a parameter, variable, resource or module. (bicep https://aka.ms/bicep/core-diagnostics#BCP063) |exportedVariable|
9+
//@[59:73) [BCP059 (Error)] The name "helperFunction" is not a function. (bicep https://aka.ms/bicep/core-diagnostics#BCP059) |helperFunction|
10+
311
var myVar = 1 + 2
412
param p = externalInput('sys.envVar', myVar)
513
//@[38:43) [BCP032 (Error)] The value must be a compile-time constant. (bicep https://aka.ms/bicep/core-diagnostics#BCP032) |myVar|

src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_ExternalInputs/parameters.formatted.bicepparam

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
using none
22

3+
import { exportedVariable, helperFunction } from 'main.bicep'
4+
5+
param p12 = '${exportedVariable}-${externalInput('custom', helperFunction())}'
6+
37
var myVar = 1 + 2
48
param p = externalInput('sys.envVar', myVar)
59

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,59 @@
11
using none
22

3+
import { exportedVariable, helperFunction } from 'main.bicep'
4+
//@[09:25) Error exportedVariable. Type: error. Declaration start char: 9, length: 16
5+
//@[27:41) Error helperFunction. Type: error. Declaration start char: 27, length: 14
6+
7+
param p12 = '${exportedVariable}-${externalInput('custom', helperFunction())}'
8+
//@[06:09) ParameterAssignment p12. Type: error. Declaration start char: 0, length: 78
9+
310
var myVar = 1 + 2
4-
//@[4:09) Variable myVar. Type: 3. Declaration start char: 0, length: 17
11+
//@[04:09) Variable myVar. Type: 3. Declaration start char: 0, length: 17
512
param p = externalInput('sys.envVar', myVar)
6-
//@[6:07) ParameterAssignment p. Type: any. Declaration start char: 0, length: 44
13+
//@[06:07) ParameterAssignment p. Type: any. Declaration start char: 0, length: 44
714

815
var x = 42
9-
//@[4:05) Variable x. Type: 42. Declaration start char: 0, length: 10
16+
//@[04:05) Variable x. Type: 42. Declaration start char: 0, length: 10
1017
var myVar2 = 'abcd-${x}'
11-
//@[4:10) Variable myVar2. Type: 'abcd-42'. Declaration start char: 0, length: 24
18+
//@[04:10) Variable myVar2. Type: 'abcd-42'. Declaration start char: 0, length: 24
1219
param p2 = externalInput('sys.envVar', myVar2)
13-
//@[6:08) ParameterAssignment p2. Type: any. Declaration start char: 0, length: 46
20+
//@[06:08) ParameterAssignment p2. Type: any. Declaration start char: 0, length: 46
1421

1522
var myVar3 = 'test'
16-
//@[4:10) Variable myVar3. Type: 'test'. Declaration start char: 0, length: 19
23+
//@[04:10) Variable myVar3. Type: 'test'. Declaration start char: 0, length: 19
1724
param p3 = externalInput(myVar3, myVar3)
18-
//@[6:08) ParameterAssignment p3. Type: any. Declaration start char: 0, length: 40
25+
//@[06:08) ParameterAssignment p3. Type: any. Declaration start char: 0, length: 40
1926

2027
var myVar4 = {
21-
//@[4:10) Variable myVar4. Type: object. Declaration start char: 0, length: 33
28+
//@[04:10) Variable myVar4. Type: object. Declaration start char: 0, length: 33
2229
name: 'test'
2330
}
2431
param p4 = externalInput('sys.cli', myVar4)
25-
//@[6:08) ParameterAssignment p4. Type: any. Declaration start char: 0, length: 43
32+
//@[06:08) ParameterAssignment p4. Type: any. Declaration start char: 0, length: 43
2633

2734
var test = 'test'
28-
//@[4:08) Variable test. Type: 'test'. Declaration start char: 0, length: 17
35+
//@[04:08) Variable test. Type: 'test'. Declaration start char: 0, length: 17
2936
var myVar5 = {
30-
//@[4:10) Variable myVar5. Type: object. Declaration start char: 0, length: 31
37+
//@[04:10) Variable myVar5. Type: object. Declaration start char: 0, length: 31
3138
name: test
3239
}
3340
param p5 = externalInput('sys.cli', {
34-
//@[6:08) ParameterAssignment p5. Type: any. Declaration start char: 0, length: 57
41+
//@[06:08) ParameterAssignment p5. Type: any. Declaration start char: 0, length: 57
3542
name: myVar5
3643
})
3744

3845
param p6 = externalInput('custom', 'test')
39-
//@[6:08) ParameterAssignment p6. Type: any. Declaration start char: 0, length: 42
46+
//@[06:08) ParameterAssignment p6. Type: any. Declaration start char: 0, length: 42
4047
param p7 = externalInput(p6)
41-
//@[6:08) ParameterAssignment p7. Type: any. Declaration start char: 0, length: 28
48+
//@[06:08) ParameterAssignment p7. Type: any. Declaration start char: 0, length: 28
4249

4350
param p8 = externalInput('custom', externalInput('custom', 'foo'))
44-
//@[6:08) ParameterAssignment p8. Type: any. Declaration start char: 0, length: 66
51+
//@[06:08) ParameterAssignment p8. Type: any. Declaration start char: 0, length: 66
4552

4653
param p9 = externalInput('custom',)
47-
//@[6:08) ParameterAssignment p9. Type: any. Declaration start char: 0, length: 35
54+
//@[06:08) ParameterAssignment p9. Type: any. Declaration start char: 0, length: 35
4855
param p10 = externalInput(, 'test')
49-
//@[6:09) ParameterAssignment p10. Type: any. Declaration start char: 0, length: 35
56+
//@[06:09) ParameterAssignment p10. Type: any. Declaration start char: 0, length: 35
5057
param p11 = externalInput('custom',foo')
51-
//@[6:09) ParameterAssignment p11. Type: error. Declaration start char: 0, length: 40
58+
//@[06:09) ParameterAssignment p11. Type: error. Declaration start char: 0, length: 40
5259

src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_ExternalInputs/parameters.syntax.bicepparam

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,61 @@
11
using none
2-
//@[00:691) ProgramSyntax
2+
//@[00:838) ProgramSyntax
33
//@[00:010) ├─UsingDeclarationSyntax
44
//@[00:005) | ├─Token(Identifier) |using|
55
//@[06:010) | ├─NoneLiteralSyntax
66
//@[06:010) | | └─Token(Identifier) |none|
77
//@[10:010) | └─SkippedTriviaSyntax
88
//@[10:014) ├─Token(NewLine) |\r\n\r\n|
99

10+
import { exportedVariable, helperFunction } from 'main.bicep'
11+
//@[00:061) ├─CompileTimeImportDeclarationSyntax
12+
//@[00:006) | ├─Token(Identifier) |import|
13+
//@[07:043) | ├─ImportedSymbolsListSyntax
14+
//@[07:008) | | ├─Token(LeftBrace) |{|
15+
//@[09:025) | | ├─ImportedSymbolsListItemSyntax
16+
//@[09:025) | | | └─IdentifierSyntax
17+
//@[09:025) | | | └─Token(Identifier) |exportedVariable|
18+
//@[25:026) | | ├─Token(Comma) |,|
19+
//@[27:041) | | ├─ImportedSymbolsListItemSyntax
20+
//@[27:041) | | | └─IdentifierSyntax
21+
//@[27:041) | | | └─Token(Identifier) |helperFunction|
22+
//@[42:043) | | └─Token(RightBrace) |}|
23+
//@[44:061) | └─CompileTimeImportFromClauseSyntax
24+
//@[44:048) | ├─Token(Identifier) |from|
25+
//@[49:061) | └─StringSyntax
26+
//@[49:061) | └─Token(StringComplete) |'main.bicep'|
27+
//@[61:065) ├─Token(NewLine) |\r\n\r\n|
28+
29+
param p12 = '${exportedVariable}-${externalInput('custom', helperFunction())}'
30+
//@[00:078) ├─ParameterAssignmentSyntax
31+
//@[00:005) | ├─Token(Identifier) |param|
32+
//@[06:009) | ├─IdentifierSyntax
33+
//@[06:009) | | └─Token(Identifier) |p12|
34+
//@[10:011) | ├─Token(Assignment) |=|
35+
//@[12:078) | └─StringSyntax
36+
//@[12:015) | ├─Token(StringLeftPiece) |'${|
37+
//@[15:031) | ├─VariableAccessSyntax
38+
//@[15:031) | | └─IdentifierSyntax
39+
//@[15:031) | | └─Token(Identifier) |exportedVariable|
40+
//@[31:035) | ├─Token(StringMiddlePiece) |}-${|
41+
//@[35:076) | ├─FunctionCallSyntax
42+
//@[35:048) | | ├─IdentifierSyntax
43+
//@[35:048) | | | └─Token(Identifier) |externalInput|
44+
//@[48:049) | | ├─Token(LeftParen) |(|
45+
//@[49:057) | | ├─FunctionArgumentSyntax
46+
//@[49:057) | | | └─StringSyntax
47+
//@[49:057) | | | └─Token(StringComplete) |'custom'|
48+
//@[57:058) | | ├─Token(Comma) |,|
49+
//@[59:075) | | ├─FunctionArgumentSyntax
50+
//@[59:075) | | | └─FunctionCallSyntax
51+
//@[59:073) | | | ├─IdentifierSyntax
52+
//@[59:073) | | | | └─Token(Identifier) |helperFunction|
53+
//@[73:074) | | | ├─Token(LeftParen) |(|
54+
//@[74:075) | | | └─Token(RightParen) |)|
55+
//@[75:076) | | └─Token(RightParen) |)|
56+
//@[76:078) | └─Token(StringRightPiece) |}'|
57+
//@[78:082) ├─Token(NewLine) |\r\n\r\n|
58+
1059
var myVar = 1 + 2
1160
//@[00:017) ├─VariableDeclarationSyntax
1261
//@[00:003) | ├─Token(Identifier) |var|

src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_ExternalInputs/parameters.tokens.bicepparam

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,35 @@ using none
33
//@[06:10) Identifier |none|
44
//@[10:14) NewLine |\r\n\r\n|
55

6+
import { exportedVariable, helperFunction } from 'main.bicep'
7+
//@[00:06) Identifier |import|
8+
//@[07:08) LeftBrace |{|
9+
//@[09:25) Identifier |exportedVariable|
10+
//@[25:26) Comma |,|
11+
//@[27:41) Identifier |helperFunction|
12+
//@[42:43) RightBrace |}|
13+
//@[44:48) Identifier |from|
14+
//@[49:61) StringComplete |'main.bicep'|
15+
//@[61:65) NewLine |\r\n\r\n|
16+
17+
param p12 = '${exportedVariable}-${externalInput('custom', helperFunction())}'
18+
//@[00:05) Identifier |param|
19+
//@[06:09) Identifier |p12|
20+
//@[10:11) Assignment |=|
21+
//@[12:15) StringLeftPiece |'${|
22+
//@[15:31) Identifier |exportedVariable|
23+
//@[31:35) StringMiddlePiece |}-${|
24+
//@[35:48) Identifier |externalInput|
25+
//@[48:49) LeftParen |(|
26+
//@[49:57) StringComplete |'custom'|
27+
//@[57:58) Comma |,|
28+
//@[59:73) Identifier |helperFunction|
29+
//@[73:74) LeftParen |(|
30+
//@[74:75) RightParen |)|
31+
//@[75:76) RightParen |)|
32+
//@[76:78) StringRightPiece |}'|
33+
//@[78:82) NewLine |\r\n\r\n|
34+
635
var myVar = 1 + 2
736
//@[00:03) Identifier |var|
837
//@[04:09) Identifier |myVar|

src/Bicep.Core/Emit/ExternalInputFunctionReferenceVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private void VisitFunctionCallSyntaxInternal(FunctionCallSyntaxBase functionCall
113113
CollectExternalInputs(functionCallSyntax, functionExpression);
114114
}
115115
}
116-
catch (ExpressionException ex) // expected if NamespaceFunctionType.EvaluatedLanguageExpression could not be evaluated
116+
catch (ExpressionException ex) // expected if NamespaceFunctionType.EvaluatedLanguageExpression could not be evaluated or error occurred when rewriting parameter assignments
117117
{
118118
this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(functionCallSyntax.Span)
119119
.FailedToEvaluateSubject("function", functionCallSyntax.ToString(), ex.Message));

src/Bicep.Core/Emit/ParameterAssignmentEvaluator.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Diagnostics;
77
using Azure.Deployments.Core.Definitions.Schema;
88
using Azure.Deployments.Core.ErrorResponses;
9+
using Azure.Deployments.Core.Exceptions;
910
using Azure.Deployments.Expression.Expressions;
1011
using Azure.Deployments.Templates.Expressions;
1112
using Azure.Deployments.Templates.Extensions;
@@ -844,7 +845,7 @@ public override Expression ReplaceWildcardImportInstanceFunctionCallExpression(W
844845
private static Expression ResultToExpression(Result result, Expression expression)
845846
=> result.Value is { } value
846847
? JTokenToExpression(value, expression.SourceSyntax)
847-
: throw new InvalidOperationException(result.Diagnostic?.Message ?? "Failed to evaluate expression.");
848+
: throw new ExpressionException(result.Diagnostic?.Message ?? "Failed to evaluate expression.");
848849

849850
private Expression EvaluateToJToken(Expression expression)
850851
{
@@ -863,9 +864,9 @@ private Expression EvaluateToJToken(Expression expression)
863864
long @long => ExpressionFactory.CreateIntegerLiteral(@long, syntax),
864865
bool @bool => ExpressionFactory.CreateBooleanLiteral(@bool, syntax),
865866
null => new NullLiteralExpression(syntax),
866-
_ => throw new InvalidOperationException($"Unrecognized JValue value of type {jValue.Value?.GetType().Name}"),
867+
_ => throw new ExpressionException($"Unrecognized JValue value of type {jValue.Value?.GetType().Name}"),
867868
},
868-
_ => throw new InvalidOperationException($"Unsupported JToken type: {token.Type}"),
869+
_ => throw new ExpressionException($"Unsupported JToken type: {token.Type}"),
869870
};
870871
}
871872
}

src/vscode-bicep-ui/package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)