From 3b87b5656d09521bf88619a4e51a71f9cfc6f1e3 Mon Sep 17 00:00:00 2001 From: Anthony Martin <38542602+anthony-c-martin@users.noreply.github.com> Date: Sun, 17 Mar 2024 03:49:17 -0400 Subject: [PATCH] Initial spread implementation --- .../EvaluationTests.cs | 28 +-- .../SpreadTests.cs | 173 ++++++++++++++++++ .../baselines/Functions_LF/main.symbols.bicep | 2 +- .../InvalidLambdas_LF/main.diagnostics.bicep | 14 +- .../InvalidLambdas_LF/main.symbols.bicep | 54 +++--- .../Lambdas_LF/main.diagnostics.bicep | 82 +++++---- .../baselines/Lambdas_LF/main.symbols.bicep | 118 ++++++------ .../Completions/symbolsPlusTypes.json | 42 +++++ .../Files/baselines/Variables_LF/main.bicep | 12 ++ .../Variables_LF/main.diagnostics.bicep | 18 +- .../Variables_LF/main.formatted.bicep | 12 ++ .../baselines/Variables_LF/main.ir.bicep | 44 ++++- .../Files/baselines/Variables_LF/main.json | 7 +- .../Variables_LF/main.sourcemap.bicep | 17 +- .../Variables_LF/main.symbolicnames.json | 7 +- .../baselines/Variables_LF/main.symbols.bicep | 15 ++ .../baselines/Variables_LF/main.syntax.bicep | 88 ++++++++- .../baselines/Variables_LF/main.tokens.bicep | 56 +++++- .../parameters.diagnostics.bicepparam | 7 +- .../parameters.symbols.bicepparam | 2 +- .../Diagnostics/ErrorBuilderTests.cs | 7 + .../Diagnostics/DiagnosticBuilder.cs | 10 + .../Emit/EmitLimitationCalculator.cs | 36 ++++ .../Intermediate/ExpressionBuilder.cs | 82 ++++++++- src/Bicep.Core/Parsing/BaseParser.cs | 28 ++- src/Bicep.Core/Parsing/Lexer.cs | 6 + src/Bicep.Core/Parsing/TokenType.cs | 1 + .../SyntaxLayouts.SyntaxVisitor.cs | 2 + src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.cs | 3 + .../Rewriters/DependsOnRemovalRewriter.cs | 6 +- src/Bicep.Core/Syntax/AstVisitor.cs | 5 + src/Bicep.Core/Syntax/CstVisitor.cs | 6 + src/Bicep.Core/Syntax/ISyntaxVisitor.cs | 2 + .../Syntax/ObjectSyntaxExtensions.cs | 7 - .../Syntax/SpreadExpressionSyntax.cs | 24 +++ src/Bicep.Core/Syntax/SyntaxFactory.cs | 1 + src/Bicep.Core/Syntax/SyntaxFacts.cs | 1 + src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs | 14 ++ src/Bicep.Core/Syntax/SyntaxVisitor.cs | 2 + .../TypeSystem/TypeAssignmentVisitor.cs | 158 ++++++++++++---- .../Files/Working/lambdas/main.bicep | 52 +++--- 41 files changed, 1009 insertions(+), 242 deletions(-) create mode 100644 src/Bicep.Core.IntegrationTests/SpreadTests.cs create mode 100644 src/Bicep.Core/Syntax/SpreadExpressionSyntax.cs diff --git a/src/Bicep.Core.IntegrationTests/EvaluationTests.cs b/src/Bicep.Core.IntegrationTests/EvaluationTests.cs index a0fdeccdbf6..77cc02fb04c 100644 --- a/src/Bicep.Core.IntegrationTests/EvaluationTests.cs +++ b/src/Bicep.Core.IntegrationTests/EvaluationTests.cs @@ -1211,45 +1211,45 @@ public void New_functions_are_evaluated_correctly() { var evaluated = TemplateEvaluator.Evaluate(template); - evaluated.Should().HaveValueAtPath("$.outputs['sayHello'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['sayHello'].value", """ [ "Hi Evie!", "Ahoy Casper!", "Hi Lady Lechuga!" ] -""")); +"""); - evaluated.Should().HaveValueAtPath("$.outputs['evenEntries'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['evenEntries'].value", """ [ "a", "c" ] -""")); +"""); evaluated.Should().HaveValueAtPath("$.outputs['concatIfEven'].value", "abcghi"); - evaluated.Should().HaveValueAtPath("$.outputs['mapValuesTest'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['mapValuesTest'].value", """ { "a": 246, "b": 912 } -""")); +"""); - evaluated.Should().HaveValueAtPath("$.outputs['objectKeysTest'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['objectKeysTest'].value", """ [ "a", "b" ] -""")); +"""); - evaluated.Should().HaveValueAtPath("$.outputs['shallowMergeTest'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['shallowMergeTest'].value", """ { "a": 123, "b": 456 } -""")); +"""); - evaluated.Should().HaveValueAtPath("$.outputs['groupByTest'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['groupByTest'].value", """ { "a": [ { @@ -1268,9 +1268,9 @@ public void New_functions_are_evaluated_correctly() } ] } -""")); +"""); - evaluated.Should().HaveValueAtPath("$.outputs['groupByWithValMapTest'].value", JToken.Parse(""" + evaluated.Should().HaveJsonAtPath("$.outputs['groupByWithValMapTest'].value", """ { "a": [ 123, @@ -1280,7 +1280,7 @@ public void New_functions_are_evaluated_correctly() 456 ] } -""")); +"""); } } } diff --git a/src/Bicep.Core.IntegrationTests/SpreadTests.cs b/src/Bicep.Core.IntegrationTests/SpreadTests.cs new file mode 100644 index 00000000000..dedfa581bdf --- /dev/null +++ b/src/Bicep.Core.IntegrationTests/SpreadTests.cs @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Bicep.Core.UnitTests.Assertions; +using Bicep.Core.UnitTests.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bicep.Core.IntegrationTests; + +[TestClass] +public class SpreadTests +{ + [TestMethod] + public void Spread_operator_results_in_correct_codegen() + { + var result = CompilationHelper.Compile(""" +var other = { + bar: [1, ...[2, 3], 4] +} + +output test object = { + foo: 'foo' + ...other + baz: 'baz' +} +"""); + + result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics(); + result.Template.Should().HaveValueAtPath("$.variables['other']['bar']", "[flatten(createArray(createArray(1), createArray(2, 3), createArray(4)))]"); + result.Template.Should().HaveValueAtPath("$.outputs['test'].value", "[shallowMerge(createArray(createObject('foo', 'foo'), variables('other'), createObject('baz', 'baz')))]"); + + var evaluated = TemplateEvaluator.Evaluate(result.Template); + evaluated.Should().HaveJsonAtPath("$.outputs['test'].value", """ +{ + "foo": "foo", + "bar": [ + 1, + 2, + 3, + 4 + ], + "baz": "baz" +} +"""); + } + + [TestMethod] + public void Spread_operator_works_on_single_line() + { + var result = CompilationHelper.Compile(""" +param other object + +var test = { foo: 'foo', ...other, baz: 'baz' } +"""); + + result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics(); + } + + [TestMethod] + public void Array_spread_cannot_be_used_inside_object() + { + var result = CompilationHelper.Compile(""" +var other = ['bar'] + +var test = { + foo: 'foo' + ...other + baz: 'baz' +} +"""); + + result.ExcludingLinterDiagnostics().Should().ContainDiagnostic( + "BCP397", Diagnostics.DiagnosticLevel.Error, """The spread operator "..." can only be used in this context for an expression assignable to type "object"."""); + } + + [TestMethod] + public void Object_spread_cannot_be_used_inside_array() + { + var result = CompilationHelper.Compile(""" +var other = { + bar: 'bar' +} + +var test = [ + 'foo' + ...other + 'baz' +] +"""); + + result.ExcludingLinterDiagnostics().Should().ContainDiagnostic( + "BCP397", Diagnostics.DiagnosticLevel.Error, """The spread operator "..." can only be used in this context for an expression assignable to type "array"."""); + } + + [TestMethod] + public void Spread_works_with_any() + { + var result = CompilationHelper.Compile(""" +var badObj = { + ...any(['foo']) +} + +var badArray = [ + ...any({ foo: 'foo' }) +] +"""); + + result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics(); + + // this will result in a runtime failure, but at least the codegen is correct. + result.Template.Should().HaveValueAtPath("$.variables['badObj']", "[shallowMerge(createArray(createArray('foo')))]"); + result.Template.Should().HaveValueAtPath("$.variables['badArray']", "[flatten(createArray(createObject('foo', 'foo')))]"); + } + + [TestMethod] + public void Spread_is_blocked_in_resource_body() + { + var result = CompilationHelper.Compile(""" +var other = { location: 'westus' } + +resource foo 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: 'foo' + ...other +} +"""); + + result.ExcludingLinterDiagnostics().Should().ContainDiagnostic( + "BCP396", Diagnostics.DiagnosticLevel.Error, """The spread operator "..." is not permitted in this location."""); + } + + [TestMethod] + public void Object_spread_edge_cases() + { + var result = CompilationHelper.Compile(""" +output test1 object = { + a: 0 + ...{ a: 1, b: 0 } + c: 0 +} + +output test2 object = { + 'ABC': 0 + ...{ 'aBC': 1 } +} + +output test3 object = { + foo: 'bar' + ...{ foo: null } +} +"""); + + result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics(); + + var evaluated = TemplateEvaluator.Evaluate(result.Template); + evaluated.Should().HaveJsonAtPath("$.outputs['test1'].value", """ +{ + "a": 1, + "b": 0, + "c": 0 +} +"""); + evaluated.Should().HaveJsonAtPath("$.outputs['test2'].value", """ +{ + "ABC": 1 +} +"""); + evaluated.Should().HaveJsonAtPath("$.outputs['test3'].value", """ +{ + "foo": null +} +"""); + } +} \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/baselines/Functions_LF/main.symbols.bicep b/src/Bicep.Core.Samples/Files/baselines/Functions_LF/main.symbols.bicep index a2d3ca065bb..ceed727cd87 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Functions_LF/main.symbols.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Functions_LF/main.symbols.bicep @@ -12,7 +12,7 @@ func sayHello(name string) string => 'Hi ${name}!' //@[05:013) Function sayHello. Type: string => string. Declaration start char: 0, length: 50 output hellos array = map(['Evie', 'Casper'], name => sayHello(name)) -//@[46:050) Local name. Type: any. Declaration start char: 46, length: 4 +//@[46:050) Local name. Type: 'Casper' | 'Evie'. Declaration start char: 46, length: 4 //@[07:013) Output hellos. Type: array. Declaration start char: 0, length: 69 func objReturnType(name string) object => { diff --git a/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.diagnostics.bicep b/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.diagnostics.bicep index e715f7fe241..9d9d56aca76 100644 --- a/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.diagnostics.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.diagnostics.bicep @@ -21,7 +21,7 @@ var map4 = map(range(0, 10), () => null) //@[29:39) [BCP070 (Error)] Argument of type "() => null" is not assignable to parameter of type "(any[, int]) => any". (CodeDescription: none) |() => null| var map5 = map(range(0, 10), (a, b, c) => a) //@[04:08) [no-unused-vars (Warning)] Variable "map5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |map5| -//@[29:43) [BCP070 (Error)] Argument of type "(any, int, any) => any" is not assignable to parameter of type "(any[, int]) => any". (CodeDescription: none) |(a, b, c) => a| +//@[29:43) [BCP070 (Error)] Argument of type "(int, int, any) => int" is not assignable to parameter of type "(any[, int]) => any". (CodeDescription: none) |(a, b, c) => a| var filter1 = filter('abc') //@[04:11) [no-unused-vars (Warning)] Variable "filter1" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filter1| @@ -37,11 +37,13 @@ var filter4 = filter(range(0, 10), () => null) //@[35:45) [BCP070 (Error)] Argument of type "() => null" is not assignable to parameter of type "(any[, int]) => bool". (CodeDescription: none) |() => null| var filter5 = filter(range(0, 10), i => i) //@[04:11) [no-unused-vars (Warning)] Variable "filter5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filter5| +//@[35:41) [BCP070 (Error)] Argument of type "int => int" is not assignable to parameter of type "(any[, int]) => bool". (CodeDescription: none) |i => i| var filter6 = filter([true, 'hello!'], i => i) //@[04:11) [no-unused-vars (Warning)] Variable "filter6" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filter6| +//@[39:45) [BCP070 (Error)] Argument of type "('hello!' | true) => ('hello!' | true)" is not assignable to parameter of type "(any[, int]) => bool". (CodeDescription: none) |i => i| var filter7 = filter(range(0, 10), (a, b, c) => true) //@[04:11) [no-unused-vars (Warning)] Variable "filter7" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filter7| -//@[35:52) [BCP070 (Error)] Argument of type "(any, int, any) => true" is not assignable to parameter of type "(any[, int]) => bool". (CodeDescription: none) |(a, b, c) => true| +//@[35:52) [BCP070 (Error)] Argument of type "(int, int, any) => true" is not assignable to parameter of type "(any[, int]) => bool". (CodeDescription: none) |(a, b, c) => true| var sort1 = sort('abc') //@[04:09) [no-unused-vars (Warning)] Variable "sort1" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sort1| @@ -57,9 +59,10 @@ var sort4 = sort(range(0, 10), () => null) //@[31:41) [BCP070 (Error)] Argument of type "() => null" is not assignable to parameter of type "(any, any) => bool". (CodeDescription: none) |() => null| var sort5 = sort(range(0, 10), i => i) //@[04:09) [no-unused-vars (Warning)] Variable "sort5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sort5| -//@[31:37) [BCP070 (Error)] Argument of type "any => any" is not assignable to parameter of type "(any, any) => bool". (CodeDescription: none) |i => i| +//@[31:37) [BCP070 (Error)] Argument of type "int => int" is not assignable to parameter of type "(any, any) => bool". (CodeDescription: none) |i => i| var sort6 = sort(range(0, 10), (i, j) => i) //@[04:09) [no-unused-vars (Warning)] Variable "sort6" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sort6| +//@[31:42) [BCP070 (Error)] Argument of type "(int, int) => int" is not assignable to parameter of type "(any, any) => bool". (CodeDescription: none) |(i, j) => i| var reduce1 = reduce('abc') //@[04:11) [no-unused-vars (Warning)] Variable "reduce1" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduce1| @@ -75,10 +78,10 @@ var reduce4 = reduce(range(0, 10), 0, () => null) //@[38:48) [BCP070 (Error)] Argument of type "() => null" is not assignable to parameter of type "(any, any[, int]) => any". (CodeDescription: none) |() => null| var reduce5 = reduce(range(0, 10), 0, i => i) //@[04:11) [no-unused-vars (Warning)] Variable "reduce5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduce5| -//@[38:44) [BCP070 (Error)] Argument of type "any => any" is not assignable to parameter of type "(any, any[, int]) => any". (CodeDescription: none) |i => i| +//@[38:44) [BCP070 (Error)] Argument of type "int => int" is not assignable to parameter of type "(any, any[, int]) => any". (CodeDescription: none) |i => i| var reduce6 = reduce(range(0, 10), 0, (a, b, c, d) => a) //@[04:11) [no-unused-vars (Warning)] Variable "reduce6" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduce6| -//@[38:55) [BCP070 (Error)] Argument of type "(any, any, int, any) => any" is not assignable to parameter of type "(any, any[, int]) => any". (CodeDescription: none) |(a, b, c, d) => a| +//@[38:55) [BCP070 (Error)] Argument of type "(int, int, int, any) => int" is not assignable to parameter of type "(any, any[, int]) => any". (CodeDescription: none) |(a, b, c, d) => a| var toObject1 = toObject('abc') //@[04:13) [no-unused-vars (Warning)] Variable "toObject1" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |toObject1| @@ -94,6 +97,7 @@ var toObject4 = toObject(range(0, 10), () => null) //@[39:49) [BCP070 (Error)] Argument of type "() => null" is not assignable to parameter of type "any => string". (CodeDescription: none) |() => null| var toObject5 = toObject(range(0, 10), i => i) //@[04:13) [no-unused-vars (Warning)] Variable "toObject5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |toObject5| +//@[39:45) [BCP070 (Error)] Argument of type "int => int" is not assignable to parameter of type "any => string". (CodeDescription: none) |i => i| var toObject6 = toObject(range(0, 10), i => '${i}', 'def') //@[04:13) [no-unused-vars (Warning)] Variable "toObject6" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |toObject6| //@[52:57) [BCP070 (Error)] Argument of type "'def'" is not assignable to parameter of type "any => any". (CodeDescription: none) |'def'| diff --git a/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.symbols.bicep b/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.symbols.bicep index 770cfff8909..4bcd992abeb 100644 --- a/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.symbols.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/InvalidLambdas_LF/main.symbols.bicep @@ -15,7 +15,7 @@ var map3 = map(range(0, 10), 'def') var map4 = map(range(0, 10), () => null) //@[04:08) Variable map4. Type: error. Declaration start char: 0, length: 40 var map5 = map(range(0, 10), (a, b, c) => a) -//@[30:31) Local a. Type: any. Declaration start char: 30, length: 1 +//@[30:31) Local a. Type: int. Declaration start char: 30, length: 1 //@[33:34) Local b. Type: int. Declaration start char: 33, length: 1 //@[36:37) Local c. Type: any. Declaration start char: 36, length: 1 //@[04:08) Variable map5. Type: error. Declaration start char: 0, length: 44 @@ -29,13 +29,13 @@ var filter3 = filter(range(0, 10), 'def') var filter4 = filter(range(0, 10), () => null) //@[04:11) Variable filter4. Type: error. Declaration start char: 0, length: 46 var filter5 = filter(range(0, 10), i => i) -//@[35:36) Local i. Type: any. Declaration start char: 35, length: 1 -//@[04:11) Variable filter5. Type: int[]. Declaration start char: 0, length: 42 +//@[35:36) Local i. Type: int. Declaration start char: 35, length: 1 +//@[04:11) Variable filter5. Type: error. Declaration start char: 0, length: 42 var filter6 = filter([true, 'hello!'], i => i) -//@[39:40) Local i. Type: any. Declaration start char: 39, length: 1 -//@[04:11) Variable filter6. Type: ('hello!' | true)[]. Declaration start char: 0, length: 46 +//@[39:40) Local i. Type: 'hello!' | true. Declaration start char: 39, length: 1 +//@[04:11) Variable filter6. Type: error. Declaration start char: 0, length: 46 var filter7 = filter(range(0, 10), (a, b, c) => true) -//@[36:37) Local a. Type: any. Declaration start char: 36, length: 1 +//@[36:37) Local a. Type: int. Declaration start char: 36, length: 1 //@[39:40) Local b. Type: int. Declaration start char: 39, length: 1 //@[42:43) Local c. Type: any. Declaration start char: 42, length: 1 //@[04:11) Variable filter7. Type: error. Declaration start char: 0, length: 53 @@ -49,12 +49,12 @@ var sort3 = sort(range(0, 10), 'def') var sort4 = sort(range(0, 10), () => null) //@[04:09) Variable sort4. Type: error. Declaration start char: 0, length: 42 var sort5 = sort(range(0, 10), i => i) -//@[31:32) Local i. Type: any. Declaration start char: 31, length: 1 +//@[31:32) Local i. Type: int. Declaration start char: 31, length: 1 //@[04:09) Variable sort5. Type: error. Declaration start char: 0, length: 38 var sort6 = sort(range(0, 10), (i, j) => i) -//@[32:33) Local i. Type: any. Declaration start char: 32, length: 1 -//@[35:36) Local j. Type: any. Declaration start char: 35, length: 1 -//@[04:09) Variable sort6. Type: int[]. Declaration start char: 0, length: 43 +//@[32:33) Local i. Type: int. Declaration start char: 32, length: 1 +//@[35:36) Local j. Type: int. Declaration start char: 35, length: 1 +//@[04:09) Variable sort6. Type: error. Declaration start char: 0, length: 43 var reduce1 = reduce('abc') //@[04:11) Variable reduce1. Type: error. Declaration start char: 0, length: 27 @@ -65,11 +65,11 @@ var reduce3 = reduce(range(0, 10), 0, 'def') var reduce4 = reduce(range(0, 10), 0, () => null) //@[04:11) Variable reduce4. Type: error. Declaration start char: 0, length: 49 var reduce5 = reduce(range(0, 10), 0, i => i) -//@[38:39) Local i. Type: any. Declaration start char: 38, length: 1 +//@[38:39) Local i. Type: int. Declaration start char: 38, length: 1 //@[04:11) Variable reduce5. Type: error. Declaration start char: 0, length: 45 var reduce6 = reduce(range(0, 10), 0, (a, b, c, d) => a) -//@[39:40) Local a. Type: any. Declaration start char: 39, length: 1 -//@[42:43) Local b. Type: any. Declaration start char: 42, length: 1 +//@[39:40) Local a. Type: int. Declaration start char: 39, length: 1 +//@[42:43) Local b. Type: int. Declaration start char: 42, length: 1 //@[45:46) Local c. Type: int. Declaration start char: 45, length: 1 //@[48:49) Local d. Type: any. Declaration start char: 48, length: 1 //@[04:11) Variable reduce6. Type: error. Declaration start char: 0, length: 56 @@ -83,13 +83,13 @@ var toObject3 = toObject(range(0, 10), 'def') var toObject4 = toObject(range(0, 10), () => null) //@[04:13) Variable toObject4. Type: error. Declaration start char: 0, length: 50 var toObject5 = toObject(range(0, 10), i => i) -//@[39:40) Local i. Type: any. Declaration start char: 39, length: 1 -//@[04:13) Variable toObject5. Type: object. Declaration start char: 0, length: 46 +//@[39:40) Local i. Type: int. Declaration start char: 39, length: 1 +//@[04:13) Variable toObject5. Type: error. Declaration start char: 0, length: 46 var toObject6 = toObject(range(0, 10), i => '${i}', 'def') -//@[39:40) Local i. Type: any. Declaration start char: 39, length: 1 +//@[39:40) Local i. Type: int. Declaration start char: 39, length: 1 //@[04:13) Variable toObject6. Type: error. Declaration start char: 0, length: 58 var toObject7 = toObject(range(0, 10), i => '${i}', () => null) -//@[39:40) Local i. Type: any. Declaration start char: 39, length: 1 +//@[39:40) Local i. Type: int. Declaration start char: 39, length: 1 //@[04:13) Variable toObject7. Type: error. Declaration start char: 0, length: 63 var ternary = map([123], true ? i => '${i}' : i => 'hello!') @@ -136,20 +136,20 @@ resource stg 'Microsoft.Storage/storageAccounts@2021-09-01' = [for i in range(0, }] output stgKeys array = map(range(0, 2), i => stg[i].listKeys().keys[0].value) -//@[40:41) Local i. Type: any. Declaration start char: 40, length: 1 +//@[40:41) Local i. Type: int. Declaration start char: 40, length: 1 //@[07:14) Output stgKeys. Type: array. Declaration start char: 0, length: 77 output stgKeys2 array = map(range(0, 2), j => stg[((j + 2) % 123)].listKeys().keys[0].value) -//@[41:42) Local j. Type: any. Declaration start char: 41, length: 1 +//@[41:42) Local j. Type: int. Declaration start char: 41, length: 1 //@[07:15) Output stgKeys2. Type: array. Declaration start char: 0, length: 92 output stgKeys3 array = map(ids, id => listKeys(id, stg[0].apiVersion).keys[0].value) //@[33:35) Local id. Type: any. Declaration start char: 33, length: 2 //@[07:15) Output stgKeys3. Type: array. Declaration start char: 0, length: 85 output accessTiers array = map(range(0, 2), k => stg[k].properties.accessTier) -//@[44:45) Local k. Type: any. Declaration start char: 44, length: 1 +//@[44:45) Local k. Type: int. Declaration start char: 44, length: 1 //@[07:18) Output accessTiers. Type: array. Declaration start char: 0, length: 78 output accessTiers2 array = map(range(0, 2), x => map(range(0, 2), y => stg[x / y].properties.accessTier)) -//@[67:68) Local y. Type: any. Declaration start char: 67, length: 1 -//@[45:46) Local x. Type: any. Declaration start char: 45, length: 1 +//@[67:68) Local y. Type: int. Declaration start char: 67, length: 1 +//@[45:46) Local x. Type: int. Declaration start char: 45, length: 1 //@[07:19) Output accessTiers2. Type: array. Declaration start char: 0, length: 106 output accessTiers3 array = map(ids, foo => reference('${foo}').accessTier) //@[37:40) Local foo. Type: any. Declaration start char: 37, length: 3 @@ -162,16 +162,16 @@ module modLoop './empty.bicep' = [for item in range(0, 5): { }] var modLoopNames = map(modLoop, i => i.name) -//@[32:33) Local i. Type: any. Declaration start char: 32, length: 1 -//@[04:16) Variable modLoopNames. Type: array. Declaration start char: 0, length: 44 +//@[32:33) Local i. Type: module. Declaration start char: 32, length: 1 +//@[04:16) Variable modLoopNames. Type: string[]. Declaration start char: 0, length: 44 output modOutputs array = map(range(0, 5), i => modLoop[i].outputs.foo) -//@[43:44) Local i. Type: any. Declaration start char: 43, length: 1 +//@[43:44) Local i. Type: int. Declaration start char: 43, length: 1 //@[07:17) Output modOutputs. Type: array. Declaration start char: 0, length: 71 var onlyComma = map([0], (,) => 'foo') //@[04:13) Variable onlyComma. Type: error. Declaration start char: 0, length: 38 var trailingCommas = map([0], (a,,) => 'foo') -//@[31:32) Local a. Type: any. Declaration start char: 31, length: 1 +//@[31:32) Local a. Type: 0. Declaration start char: 31, length: 1 //@[04:18) Variable trailingCommas. Type: 'foo'[]. Declaration start char: 0, length: 45 var multiLineOnly = map([0], ( //@[04:17) Variable multiLineOnly. Type: any. Declaration start char: 0, length: 51 @@ -182,7 +182,7 @@ var multiLineOnly = map([0], ( var multiLineTrailingCommas = map([0], ( //@[04:27) Variable multiLineTrailingCommas. Type: 'foo'[]. Declaration start char: 0, length: 60 a, -//@[02:03) Local a. Type: any. Declaration start char: 2, length: 1 +//@[02:03) Local a. Type: 0. Declaration start char: 2, length: 1 ,) => 'foo') var lineBeforeComma = map([0], ( diff --git a/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.diagnostics.bicep b/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.diagnostics.bicep index 2feac80d649..18e0ce6172c 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.diagnostics.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.diagnostics.bicep @@ -10,54 +10,54 @@ func isEven(i int) bool => i % 2 == 0 var numbers = range(0, 4) var sayHello = map(doggos, i => 'Hello ${i}!') -//@[4:12) [no-unused-vars (Warning)] Variable "sayHello" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sayHello| +//@[04:012) [no-unused-vars (Warning)] Variable "sayHello" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sayHello| // optional index parameter for map lambda var sayHello2 = map(doggos, (dog, i) => '${isEven(i) ? 'Hi' : 'Ahoy'} ${dog}!') -//@[4:13) [no-unused-vars (Warning)] Variable "sayHello2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sayHello2| +//@[04:013) [no-unused-vars (Warning)] Variable "sayHello2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sayHello2| var evenNumbers = filter(numbers, i => isEven(i)) -//@[4:15) [no-unused-vars (Warning)] Variable "evenNumbers" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenNumbers| +//@[04:015) [no-unused-vars (Warning)] Variable "evenNumbers" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenNumbers| // optional index parameter for filter lambda var evenEntries = filter(['a', 'b', 'c', 'd'], (item, i) => isEven(i)) -//@[4:15) [no-unused-vars (Warning)] Variable "evenEntries" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenEntries| +//@[04:015) [no-unused-vars (Warning)] Variable "evenEntries" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenEntries| var evenDoggosNestedLambdas = map(filter(numbers, i => contains(filter(numbers, j => 0 == j % 2), i)), x => doggos[x]) -//@[4:27) [no-unused-vars (Warning)] Variable "evenDoggosNestedLambdas" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenDoggosNestedLambdas| +//@[04:027) [no-unused-vars (Warning)] Variable "evenDoggosNestedLambdas" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenDoggosNestedLambdas| var flattenedArrayOfArrays = flatten([[0, 1], [2, 3], [4, 5]]) -//@[4:26) [no-unused-vars (Warning)] Variable "flattenedArrayOfArrays" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedArrayOfArrays| +//@[04:026) [no-unused-vars (Warning)] Variable "flattenedArrayOfArrays" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedArrayOfArrays| var flattenedEmptyArray = flatten([]) -//@[4:23) [no-unused-vars (Warning)] Variable "flattenedEmptyArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedEmptyArray| +//@[04:023) [no-unused-vars (Warning)] Variable "flattenedEmptyArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedEmptyArray| var mapSayHi = map(['abc', 'def', 'ghi'], foo => 'Hi ${foo}!') -//@[4:12) [no-unused-vars (Warning)] Variable "mapSayHi" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapSayHi| +//@[04:012) [no-unused-vars (Warning)] Variable "mapSayHi" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapSayHi| var mapEmpty = map([], foo => 'Hi ${foo}!') -//@[4:12) [no-unused-vars (Warning)] Variable "mapEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapEmpty| +//@[04:012) [no-unused-vars (Warning)] Variable "mapEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapEmpty| var mapObject = map(range(0, length(doggos)), i => { i: i doggo: doggos[i] greeting: 'Ahoy, ${doggos[i]}!' }) var mapArray = flatten(map(range(1, 3), i => [i * 2, (i * 2) + 1])) -//@[4:12) [no-unused-vars (Warning)] Variable "mapArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapArray| +//@[04:012) [no-unused-vars (Warning)] Variable "mapArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapArray| var mapMultiLineArray = flatten(map(range(1, 3), i => [ -//@[4:21) [no-unused-vars (Warning)] Variable "mapMultiLineArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapMultiLineArray| +//@[04:021) [no-unused-vars (Warning)] Variable "mapMultiLineArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapMultiLineArray| i * 3 (i * 3) + 1 (i * 3) + 2 ])) var filterEqualityCheck = filter(['abc', 'def', 'ghi'], foo => 'def' == foo) -//@[4:23) [no-unused-vars (Warning)] Variable "filterEqualityCheck" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEqualityCheck| +//@[04:023) [no-unused-vars (Warning)] Variable "filterEqualityCheck" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEqualityCheck| var filterEmpty = filter([], foo => 'def' == foo) -//@[4:15) [no-unused-vars (Warning)] Variable "filterEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEmpty| +//@[04:015) [no-unused-vars (Warning)] Variable "filterEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEmpty| var sortNumeric = sort([8, 3, 10, -13, 5], (x, y) => x < y) -//@[4:15) [no-unused-vars (Warning)] Variable "sortNumeric" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortNumeric| +//@[04:015) [no-unused-vars (Warning)] Variable "sortNumeric" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortNumeric| var sortAlpha = sort(['ghi', 'abc', 'def'], (x, y) => x < y) -//@[4:13) [no-unused-vars (Warning)] Variable "sortAlpha" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlpha| +//@[04:013) [no-unused-vars (Warning)] Variable "sortAlpha" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlpha| var sortAlphaReverse = sort(['ghi', 'abc', 'def'], (x, y) => x > y) -//@[4:20) [no-unused-vars (Warning)] Variable "sortAlphaReverse" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlphaReverse| +//@[04:020) [no-unused-vars (Warning)] Variable "sortAlphaReverse" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlphaReverse| var sortByObjectKey = sort([ { key: 124, name: 'Second' } { key: 298, name: 'Third' } @@ -65,26 +65,28 @@ var sortByObjectKey = sort([ { key: 1232, name: 'Fourth' } ], (x, y) => int(x.key) < int(y.key)) var sortEmpty = sort([], (x, y) => int(x) < int(y)) -//@[4:13) [no-unused-vars (Warning)] Variable "sortEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortEmpty| +//@[04:013) [no-unused-vars (Warning)] Variable "sortEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortEmpty| var reduceStringConcat = reduce(['abc', 'def', 'ghi'], '', (cur, next) => concat(cur, next)) -//@[4:22) [no-unused-vars (Warning)] Variable "reduceStringConcat" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceStringConcat| +//@[04:022) [no-unused-vars (Warning)] Variable "reduceStringConcat" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceStringConcat| +//@[74:091) [prefer-interpolation (Warning)] Use string interpolation instead of the concat function. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-interpolation)) |concat(cur, next)| var reduceStringConcatEven = reduce(['abc', 'def', 'ghi'], '', (cur, next, i) => isEven(i) ? concat(cur, next) : cur) -//@[4:26) [no-unused-vars (Warning)] Variable "reduceStringConcatEven" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceStringConcatEven| +//@[04:026) [no-unused-vars (Warning)] Variable "reduceStringConcatEven" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceStringConcatEven| +//@[93:110) [prefer-interpolation (Warning)] Use string interpolation instead of the concat function. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-interpolation)) |concat(cur, next)| var reduceFactorial = reduce(range(1, 5), 1, (cur, next) => cur * next) -//@[4:19) [no-unused-vars (Warning)] Variable "reduceFactorial" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceFactorial| +//@[04:019) [no-unused-vars (Warning)] Variable "reduceFactorial" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceFactorial| var reduceObjectUnion = reduce([ -//@[4:21) [no-unused-vars (Warning)] Variable "reduceObjectUnion" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceObjectUnion| +//@[04:021) [no-unused-vars (Warning)] Variable "reduceObjectUnion" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceObjectUnion| { foo: 123 } { bar: 456 } { baz: 789 } ], {}, (cur, next) => union(cur, next)) var reduceEmpty = reduce([], 0, (cur, next) => cur) -//@[4:15) [no-unused-vars (Warning)] Variable "reduceEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceEmpty| +//@[04:015) [no-unused-vars (Warning)] Variable "reduceEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceEmpty| var itemForLoop = [for item in range(0, 10): item] var filteredLoop = filter(itemForLoop, i => i > 5) -//@[4:16) [no-unused-vars (Warning)] Variable "filteredLoop" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filteredLoop| +//@[04:016) [no-unused-vars (Warning)] Variable "filteredLoop" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filteredLoop| output doggoGreetings array = [for item in mapObject: item.greeting] @@ -92,7 +94,7 @@ resource storageAcc 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { name: 'asdfsadf' } var mappedResProps = map(items(storageAcc.properties.secondaryEndpoints), item => item.value) -//@[4:18) [no-unused-vars (Warning)] Variable "mappedResProps" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mappedResProps| +//@[04:018) [no-unused-vars (Warning)] Variable "mappedResProps" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mappedResProps| module myMod './test.bicep' = { name: 'asdfsadf' @@ -101,74 +103,76 @@ module myMod './test.bicep' = { } } var mappedModOutputProps = map(myMod.outputs.outputThis, doggo => '${doggo} says bork') -//@[4:24) [no-unused-vars (Warning)] Variable "mappedModOutputProps" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mappedModOutputProps| +//@[04:024) [no-unused-vars (Warning)] Variable "mappedModOutputProps" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mappedModOutputProps| var parentheses = map([123], (i => '${i}')) -//@[4:15) [no-unused-vars (Warning)] Variable "parentheses" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |parentheses| +//@[04:015) [no-unused-vars (Warning)] Variable "parentheses" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |parentheses| var objectMap = toObject([123, 456, 789], i => '${i / 100}') -//@[4:13) [no-unused-vars (Warning)] Variable "objectMap" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap| +//@[04:013) [no-unused-vars (Warning)] Variable "objectMap" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap| var objectMap2 = toObject(range(0, 10), i => '${i}', i => { -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap2| +//@[04:014) [no-unused-vars (Warning)] Variable "objectMap2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap2| isEven: (i % 2) == 0 isGreaterThan4: (i > 4) }) var objectMap3 = toObject(sortByObjectKey, x => x.name) -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap3" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap3| +//@[04:014) [no-unused-vars (Warning)] Variable "objectMap3" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap3| var objectMap4 = toObject(sortByObjectKey, x => -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap4" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap4| +//@[04:014) [no-unused-vars (Warning)] Variable "objectMap4" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap4| x.name) var objectMap5 = toObject(sortByObjectKey, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx => xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.name) -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap5| +//@[04:014) [no-unused-vars (Warning)] Variable "objectMap5" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap5| var objectMap6 = toObject(range(0, 10), i => '${i}', i => // comment -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap6" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap6| +//@[04:014) [no-unused-vars (Warning)] Variable "objectMap6" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap6| { isEven: (i % 2) == 0 isGreaterThan4: (i > 4) }) var multiLine = reduce(['abc', 'def', 'ghi'], '', ( -//@[4:13) [no-unused-vars (Warning)] Variable "multiLine" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |multiLine| +//@[04:013) [no-unused-vars (Warning)] Variable "multiLine" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |multiLine| cur, next ) => concat(cur, next)) +//@[05:022) [prefer-interpolation (Warning)] Use string interpolation instead of the concat function. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-interpolation)) |concat(cur, next)| var multiLineWithComment = reduce(['abc', 'def', 'ghi'], '', ( -//@[4:24) [no-unused-vars (Warning)] Variable "multiLineWithComment" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |multiLineWithComment| +//@[04:024) [no-unused-vars (Warning)] Variable "multiLineWithComment" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |multiLineWithComment| // comment cur, next ) => concat(cur, next)) +//@[05:022) [prefer-interpolation (Warning)] Use string interpolation instead of the concat function. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-interpolation)) |concat(cur, next)| var mapVals = mapValues({ -//@[4:11) [no-unused-vars (Warning)] Variable "mapVals" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapVals| +//@[04:011) [no-unused-vars (Warning)] Variable "mapVals" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapVals| a: 123 b: 456 }, val => val * 2) var objectKeysTest = objectKeys({ -//@[4:18) [no-unused-vars (Warning)] Variable "objectKeysTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectKeysTest| +//@[04:018) [no-unused-vars (Warning)] Variable "objectKeysTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectKeysTest| a: 123 b: 456 }) var shallowMergeTest = shallowMerge([{ -//@[4:20) [no-unused-vars (Warning)] Variable "shallowMergeTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |shallowMergeTest| +//@[04:020) [no-unused-vars (Warning)] Variable "shallowMergeTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |shallowMergeTest| a: 123 }, { b: 456 }]) var groupByTest = groupBy([ -//@[4:15) [no-unused-vars (Warning)] Variable "groupByTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |groupByTest| +//@[04:015) [no-unused-vars (Warning)] Variable "groupByTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |groupByTest| { type: 'a', value: 123 } { type: 'b', value: 456 } { type: 'a', value: 789 } ], arg => arg.type) var groupByWithValMapTest = groupBy([ -//@[4:25) [no-unused-vars (Warning)] Variable "groupByWithValMapTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |groupByWithValMapTest| +//@[04:025) [no-unused-vars (Warning)] Variable "groupByWithValMapTest" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |groupByWithValMapTest| { type: 'a', value: 123 } { type: 'b', value: 456 } { type: 'a', value: 789 } diff --git a/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.symbols.bicep b/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.symbols.bicep index 9e3f19c40bf..88fa8efaaa5 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.symbols.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Lambdas_LF/main.symbols.bicep @@ -14,27 +14,27 @@ var numbers = range(0, 4) //@[004:011) Variable numbers. Type: int[]. Declaration start char: 0, length: 25 var sayHello = map(doggos, i => 'Hello ${i}!') -//@[027:028) Local i. Type: any. Declaration start char: 27, length: 1 +//@[027:028) Local i. Type: 'Casper' | 'Evie' | 'Indy' | 'Kira'. Declaration start char: 27, length: 1 //@[004:012) Variable sayHello. Type: string[]. Declaration start char: 0, length: 46 // optional index parameter for map lambda var sayHello2 = map(doggos, (dog, i) => '${isEven(i) ? 'Hi' : 'Ahoy'} ${dog}!') -//@[029:032) Local dog. Type: any. Declaration start char: 29, length: 3 +//@[029:032) Local dog. Type: 'Casper' | 'Evie' | 'Indy' | 'Kira'. Declaration start char: 29, length: 3 //@[034:035) Local i. Type: int. Declaration start char: 34, length: 1 //@[004:013) Variable sayHello2. Type: string[]. Declaration start char: 0, length: 79 var evenNumbers = filter(numbers, i => isEven(i)) -//@[034:035) Local i. Type: any. Declaration start char: 34, length: 1 +//@[034:035) Local i. Type: int. Declaration start char: 34, length: 1 //@[004:015) Variable evenNumbers. Type: int[]. Declaration start char: 0, length: 49 // optional index parameter for filter lambda var evenEntries = filter(['a', 'b', 'c', 'd'], (item, i) => isEven(i)) -//@[048:052) Local item. Type: any. Declaration start char: 48, length: 4 +//@[048:052) Local item. Type: 'a' | 'b' | 'c' | 'd'. Declaration start char: 48, length: 4 //@[054:055) Local i. Type: int. Declaration start char: 54, length: 1 //@[004:015) Variable evenEntries. Type: ('a' | 'b' | 'c' | 'd')[]. Declaration start char: 0, length: 70 var evenDoggosNestedLambdas = map(filter(numbers, i => contains(filter(numbers, j => 0 == j % 2), i)), x => doggos[x]) -//@[080:081) Local j. Type: any. Declaration start char: 80, length: 1 -//@[050:051) Local i. Type: any. Declaration start char: 50, length: 1 -//@[103:104) Local x. Type: any. Declaration start char: 103, length: 1 +//@[080:081) Local j. Type: int. Declaration start char: 80, length: 1 +//@[050:051) Local i. Type: int. Declaration start char: 50, length: 1 +//@[103:104) Local x. Type: int. Declaration start char: 103, length: 1 //@[004:027) Variable evenDoggosNestedLambdas. Type: ('Casper' | 'Evie' | 'Indy' | 'Kira')[]. Declaration start char: 0, length: 118 var flattenedArrayOfArrays = flatten([[0, 1], [2, 3], [4, 5]]) @@ -43,23 +43,23 @@ var flattenedEmptyArray = flatten([]) //@[004:023) Variable flattenedEmptyArray. Type: . Declaration start char: 0, length: 37 var mapSayHi = map(['abc', 'def', 'ghi'], foo => 'Hi ${foo}!') -//@[042:045) Local foo. Type: any. Declaration start char: 42, length: 3 +//@[042:045) Local foo. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 42, length: 3 //@[004:012) Variable mapSayHi. Type: string[]. Declaration start char: 0, length: 62 var mapEmpty = map([], foo => 'Hi ${foo}!') -//@[023:026) Local foo. Type: any. Declaration start char: 23, length: 3 +//@[023:026) Local foo. Type: never. Declaration start char: 23, length: 3 //@[004:012) Variable mapEmpty. Type: string[]. Declaration start char: 0, length: 43 var mapObject = map(range(0, length(doggos)), i => { -//@[046:047) Local i. Type: any. Declaration start char: 46, length: 1 +//@[046:047) Local i. Type: int. Declaration start char: 46, length: 1 //@[004:013) Variable mapObject. Type: object[]. Declaration start char: 0, length: 115 i: i doggo: doggos[i] greeting: 'Ahoy, ${doggos[i]}!' }) var mapArray = flatten(map(range(1, 3), i => [i * 2, (i * 2) + 1])) -//@[040:041) Local i. Type: any. Declaration start char: 40, length: 1 +//@[040:041) Local i. Type: int. Declaration start char: 40, length: 1 //@[004:012) Variable mapArray. Type: int[]. Declaration start char: 0, length: 67 var mapMultiLineArray = flatten(map(range(1, 3), i => [ -//@[049:050) Local i. Type: any. Declaration start char: 49, length: 1 +//@[049:050) Local i. Type: int. Declaration start char: 49, length: 1 //@[004:021) Variable mapMultiLineArray. Type: int[]. Declaration start char: 0, length: 95 i * 3 (i * 3) + 1 @@ -67,23 +67,23 @@ var mapMultiLineArray = flatten(map(range(1, 3), i => [ ])) var filterEqualityCheck = filter(['abc', 'def', 'ghi'], foo => 'def' == foo) -//@[056:059) Local foo. Type: any. Declaration start char: 56, length: 3 +//@[056:059) Local foo. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 56, length: 3 //@[004:023) Variable filterEqualityCheck. Type: ('abc' | 'def' | 'ghi')[]. Declaration start char: 0, length: 76 var filterEmpty = filter([], foo => 'def' == foo) -//@[029:032) Local foo. Type: any. Declaration start char: 29, length: 3 +//@[029:032) Local foo. Type: never. Declaration start char: 29, length: 3 //@[004:015) Variable filterEmpty. Type: never[]. Declaration start char: 0, length: 49 var sortNumeric = sort([8, 3, 10, -13, 5], (x, y) => x < y) -//@[044:045) Local x. Type: any. Declaration start char: 44, length: 1 -//@[047:048) Local y. Type: any. Declaration start char: 47, length: 1 +//@[044:045) Local x. Type: -13 | 10 | 3 | 5 | 8. Declaration start char: 44, length: 1 +//@[047:048) Local y. Type: -13 | 10 | 3 | 5 | 8. Declaration start char: 47, length: 1 //@[004:015) Variable sortNumeric. Type: (-13 | 10 | 3 | 5 | 8)[]. Declaration start char: 0, length: 59 var sortAlpha = sort(['ghi', 'abc', 'def'], (x, y) => x < y) -//@[045:046) Local x. Type: any. Declaration start char: 45, length: 1 -//@[048:049) Local y. Type: any. Declaration start char: 48, length: 1 +//@[045:046) Local x. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 45, length: 1 +//@[048:049) Local y. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 48, length: 1 //@[004:013) Variable sortAlpha. Type: ('abc' | 'def' | 'ghi')[]. Declaration start char: 0, length: 60 var sortAlphaReverse = sort(['ghi', 'abc', 'def'], (x, y) => x > y) -//@[052:053) Local x. Type: any. Declaration start char: 52, length: 1 -//@[055:056) Local y. Type: any. Declaration start char: 55, length: 1 +//@[052:053) Local x. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 52, length: 1 +//@[055:056) Local y. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 55, length: 1 //@[004:020) Variable sortAlphaReverse. Type: ('abc' | 'def' | 'ghi')[]. Declaration start char: 0, length: 67 var sortByObjectKey = sort([ //@[004:019) Variable sortByObjectKey. Type: (object | object | object | object)[]. Declaration start char: 0, length: 188 @@ -92,44 +92,44 @@ var sortByObjectKey = sort([ { key: 24, name: 'First' } { key: 1232, name: 'Fourth' } ], (x, y) => int(x.key) < int(y.key)) -//@[004:005) Local x. Type: any. Declaration start char: 4, length: 1 -//@[007:008) Local y. Type: any. Declaration start char: 7, length: 1 +//@[004:005) Local x. Type: object | object | object | object. Declaration start char: 4, length: 1 +//@[007:008) Local y. Type: object | object | object | object. Declaration start char: 7, length: 1 var sortEmpty = sort([], (x, y) => int(x) < int(y)) -//@[026:027) Local x. Type: any. Declaration start char: 26, length: 1 -//@[029:030) Local y. Type: any. Declaration start char: 29, length: 1 +//@[026:027) Local x. Type: never. Declaration start char: 26, length: 1 +//@[029:030) Local y. Type: never. Declaration start char: 29, length: 1 //@[004:013) Variable sortEmpty. Type: never[]. Declaration start char: 0, length: 51 var reduceStringConcat = reduce(['abc', 'def', 'ghi'], '', (cur, next) => concat(cur, next)) -//@[060:063) Local cur. Type: any. Declaration start char: 60, length: 3 -//@[065:069) Local next. Type: any. Declaration start char: 65, length: 4 -//@[004:022) Variable reduceStringConcat. Type: any. Declaration start char: 0, length: 92 +//@[060:063) Local cur. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 60, length: 3 +//@[065:069) Local next. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 65, length: 4 +//@[004:022) Variable reduceStringConcat. Type: string. Declaration start char: 0, length: 92 var reduceStringConcatEven = reduce(['abc', 'def', 'ghi'], '', (cur, next, i) => isEven(i) ? concat(cur, next) : cur) -//@[064:067) Local cur. Type: any. Declaration start char: 64, length: 3 -//@[069:073) Local next. Type: any. Declaration start char: 69, length: 4 +//@[064:067) Local cur. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 64, length: 3 +//@[069:073) Local next. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 69, length: 4 //@[075:076) Local i. Type: int. Declaration start char: 75, length: 1 -//@[004:026) Variable reduceStringConcatEven. Type: any. Declaration start char: 0, length: 117 +//@[004:026) Variable reduceStringConcatEven. Type: 'abc' | 'def' | 'ghi' | string. Declaration start char: 0, length: 117 var reduceFactorial = reduce(range(1, 5), 1, (cur, next) => cur * next) -//@[046:049) Local cur. Type: any. Declaration start char: 46, length: 3 -//@[051:055) Local next. Type: any. Declaration start char: 51, length: 4 +//@[046:049) Local cur. Type: int. Declaration start char: 46, length: 3 +//@[051:055) Local next. Type: int. Declaration start char: 51, length: 4 //@[004:019) Variable reduceFactorial. Type: int. Declaration start char: 0, length: 71 var reduceObjectUnion = reduce([ -//@[004:021) Variable reduceObjectUnion. Type: any. Declaration start char: 0, length: 117 +//@[004:021) Variable reduceObjectUnion. Type: object. Declaration start char: 0, length: 117 { foo: 123 } { bar: 456 } { baz: 789 } ], {}, (cur, next) => union(cur, next)) -//@[008:011) Local cur. Type: any. Declaration start char: 8, length: 3 -//@[013:017) Local next. Type: any. Declaration start char: 13, length: 4 +//@[008:011) Local cur. Type: object | object | object. Declaration start char: 8, length: 3 +//@[013:017) Local next. Type: object | object | object. Declaration start char: 13, length: 4 var reduceEmpty = reduce([], 0, (cur, next) => cur) -//@[033:036) Local cur. Type: any. Declaration start char: 33, length: 3 -//@[038:042) Local next. Type: any. Declaration start char: 38, length: 4 -//@[004:015) Variable reduceEmpty. Type: any. Declaration start char: 0, length: 51 +//@[033:036) Local cur. Type: never. Declaration start char: 33, length: 3 +//@[038:042) Local next. Type: never. Declaration start char: 38, length: 4 +//@[004:015) Variable reduceEmpty. Type: never. Declaration start char: 0, length: 51 var itemForLoop = [for item in range(0, 10): item] //@[023:027) Local item. Type: int. Declaration start char: 23, length: 4 //@[004:015) Variable itemForLoop. Type: int[]. Declaration start char: 0, length: 50 var filteredLoop = filter(itemForLoop, i => i > 5) -//@[039:040) Local i. Type: any. Declaration start char: 39, length: 1 +//@[039:040) Local i. Type: int. Declaration start char: 39, length: 1 //@[004:016) Variable filteredLoop. Type: int[]. Declaration start char: 0, length: 50 output doggoGreetings array = [for item in mapObject: item.greeting] @@ -141,7 +141,7 @@ resource storageAcc 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { name: 'asdfsadf' } var mappedResProps = map(items(storageAcc.properties.secondaryEndpoints), item => item.value) -//@[074:078) Local item. Type: any. Declaration start char: 74, length: 4 +//@[074:078) Local item. Type: object. Declaration start char: 74, length: 4 //@[004:018) Variable mappedResProps. Type: array. Declaration start char: 0, length: 93 module myMod './test.bicep' = { @@ -149,7 +149,7 @@ module myMod './test.bicep' = { name: 'asdfsadf' params: { outputThis: map(mapObject, obj => obj.doggo) -//@[031:034) Local obj. Type: any. Declaration start char: 31, length: 3 +//@[031:034) Local obj. Type: object. Declaration start char: 31, length: 3 } } var mappedModOutputProps = map(myMod.outputs.outputThis, doggo => '${doggo} says bork') @@ -161,29 +161,29 @@ var parentheses = map([123], (i => '${i}')) //@[004:015) Variable parentheses. Type: string[]. Declaration start char: 0, length: 43 var objectMap = toObject([123, 456, 789], i => '${i / 100}') -//@[042:043) Local i. Type: any. Declaration start char: 42, length: 1 +//@[042:043) Local i. Type: 123 | 456 | 789. Declaration start char: 42, length: 1 //@[004:013) Variable objectMap. Type: object. Declaration start char: 0, length: 60 var objectMap2 = toObject(range(0, 10), i => '${i}', i => { -//@[040:041) Local i. Type: any. Declaration start char: 40, length: 1 -//@[053:054) Local i. Type: any. Declaration start char: 53, length: 1 +//@[040:041) Local i. Type: int. Declaration start char: 40, length: 1 +//@[053:054) Local i. Type: int. Declaration start char: 53, length: 1 //@[004:014) Variable objectMap2. Type: object. Declaration start char: 0, length: 111 isEven: (i % 2) == 0 isGreaterThan4: (i > 4) }) var objectMap3 = toObject(sortByObjectKey, x => x.name) -//@[043:044) Local x. Type: any. Declaration start char: 43, length: 1 +//@[043:044) Local x. Type: object | object | object | object. Declaration start char: 43, length: 1 //@[004:014) Variable objectMap3. Type: object. Declaration start char: 0, length: 55 var objectMap4 = toObject(sortByObjectKey, x => -//@[043:044) Local x. Type: any. Declaration start char: 43, length: 1 +//@[043:044) Local x. Type: object | object | object | object. Declaration start char: 43, length: 1 //@[004:014) Variable objectMap4. Type: object. Declaration start char: 0, length: 60 x.name) var objectMap5 = toObject(sortByObjectKey, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx => xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.name) -//@[043:081) Local xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. Type: any. Declaration start char: 43, length: 38 +//@[043:081) Local xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. Type: object | object | object | object. Declaration start char: 43, length: 38 //@[004:014) Variable objectMap5. Type: object. Declaration start char: 0, length: 129 var objectMap6 = toObject(range(0, 10), i => '${i}', i => // comment -//@[040:041) Local i. Type: any. Declaration start char: 40, length: 1 -//@[053:054) Local i. Type: any. Declaration start char: 53, length: 1 +//@[040:041) Local i. Type: int. Declaration start char: 40, length: 1 +//@[053:054) Local i. Type: int. Declaration start char: 53, length: 1 //@[004:014) Variable objectMap6. Type: object. Declaration start char: 0, length: 122 { isEven: (i % 2) == 0 @@ -191,20 +191,20 @@ var objectMap6 = toObject(range(0, 10), i => '${i}', i => // comment }) var multiLine = reduce(['abc', 'def', 'ghi'], '', ( -//@[004:013) Variable multiLine. Type: any. Declaration start char: 0, length: 89 +//@[004:013) Variable multiLine. Type: string. Declaration start char: 0, length: 89 cur, -//@[002:005) Local cur. Type: any. Declaration start char: 2, length: 3 +//@[002:005) Local cur. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 2, length: 3 next -//@[002:006) Local next. Type: any. Declaration start char: 2, length: 4 +//@[002:006) Local next. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 2, length: 4 ) => concat(cur, next)) var multiLineWithComment = reduce(['abc', 'def', 'ghi'], '', ( -//@[004:024) Variable multiLineWithComment. Type: any. Declaration start char: 0, length: 113 +//@[004:024) Variable multiLineWithComment. Type: string. Declaration start char: 0, length: 113 // comment cur, -//@[002:005) Local cur. Type: any. Declaration start char: 2, length: 3 +//@[002:005) Local cur. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 2, length: 3 next -//@[002:006) Local next. Type: any. Declaration start char: 2, length: 4 +//@[002:006) Local next. Type: 'abc' | 'def' | 'ghi'. Declaration start char: 2, length: 4 ) => concat(cur, next)) var mapVals = mapValues({ @@ -212,7 +212,7 @@ var mapVals = mapValues({ a: 123 b: 456 }, val => val * 2) -//@[003:006) Local val. Type: any. Declaration start char: 3, length: 3 +//@[003:006) Local val. Type: 123 | 456. Declaration start char: 3, length: 3 var objectKeysTest = objectKeys({ //@[004:018) Variable objectKeysTest. Type: ('a' | 'b')[]. Declaration start char: 0, length: 54 @@ -233,7 +233,7 @@ var groupByTest = groupBy([ { type: 'b', value: 456 } { type: 'a', value: 789 } ], arg => arg.type) -//@[003:006) Local arg. Type: any. Declaration start char: 3, length: 3 +//@[003:006) Local arg. Type: object | object | object. Declaration start char: 3, length: 3 var groupByWithValMapTest = groupBy([ //@[004:025) Variable groupByWithValMapTest. Type: object. Declaration start char: 0, length: 159 @@ -241,6 +241,6 @@ var groupByWithValMapTest = groupBy([ { type: 'b', value: 456 } { type: 'a', value: 789 } ], arg => arg.type, arg => arg.value) -//@[003:006) Local arg. Type: any. Declaration start char: 3, length: 3 -//@[020:023) Local arg. Type: any. Declaration start char: 20, length: 3 +//@[003:006) Local arg. Type: object | object | object. Declaration start char: 3, length: 3 +//@[020:023) Local arg. Type: object | object | object. Declaration start char: 20, length: 3 diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/Completions/symbolsPlusTypes.json b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/Completions/symbolsPlusTypes.json index 20572ae7f6a..aef989af71e 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/Completions/symbolsPlusTypes.json +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/Completions/symbolsPlusTypes.json @@ -265,6 +265,20 @@ "newText": "arrayOfStringsViaLoop" } }, + { + "label": "arraySpread", + "kind": "variable", + "detail": "arraySpread", + "deprecated": false, + "preselect": false, + "sortText": "2_arraySpread", + "insertTextFormat": "plainText", + "insertTextMode": "adjustIndentation", + "textEdit": { + "range": {}, + "newText": "arraySpread" + } + }, { "label": "az", "kind": "folder", @@ -2849,6 +2863,20 @@ "command": "editor.action.triggerParameterHints" } }, + { + "label": "spread", + "kind": "variable", + "detail": "spread", + "deprecated": false, + "preselect": false, + "sortText": "2_spread", + "insertTextFormat": "plainText", + "insertTextMode": "adjustIndentation", + "textEdit": { + "range": {}, + "newText": "spread" + } + }, { "label": "startsWith", "kind": "function", @@ -3087,6 +3115,20 @@ "command": "editor.action.triggerParameterHints" } }, + { + "label": "test", + "kind": "variable", + "detail": "test", + "deprecated": false, + "preselect": false, + "sortText": "2_test", + "insertTextFormat": "plainText", + "insertTextMode": "adjustIndentation", + "textEdit": { + "range": {}, + "newText": "test" + } + }, { "label": "toLower", "kind": "function", diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.bicep index f99c11841a0..e787a698ca7 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.bicep @@ -358,3 +358,15 @@ var joinedString = join(['I', 'love', 'Bicep!'], ' ') var prefix = take('food', 3) var isPrefixed = startsWith('food', 'foo') + +var spread = { + foo: 'abc' + ...issue1332 +} + +var test = { + ...spread + bar: 'def' +} + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.diagnostics.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.diagnostics.bicep index 6c81d44d25f..7b010b2874f 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.diagnostics.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.diagnostics.bicep @@ -296,7 +296,6 @@ var scopesWithArmRepresentation = { // Issue #1332 var issue1332_propname = 'ptest' var issue1332 = true ? { -//@[04:13) [no-unused-vars (Warning)] Variable "issue1332" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |issue1332| prop1: { '${issue1332_propname}': {} } @@ -364,13 +363,10 @@ var arrayOfArraysViaLoop = [for (name, i) in loopInput: [ 'prefix-${i}-${name}-suffix' ]] var arrayOfBooleans = [for (name, i) in loopInput: i % 2 == 0] -//@[04:19) [no-unused-vars (Warning)] Variable "arrayOfBooleans" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |arrayOfBooleans| var arrayOfHardCodedNumbers = [for i in range(0,10): 3] -//@[04:27) [no-unused-vars (Warning)] Variable "arrayOfHardCodedNumbers" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |arrayOfHardCodedNumbers| var arrayOfHardCodedBools = [for i in range(0,10): false] //@[04:25) [no-unused-vars (Warning)] Variable "arrayOfHardCodedBools" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |arrayOfHardCodedBools| var arrayOfHardCodedStrings = [for i in range(0,3): 'hi'] -//@[04:27) [no-unused-vars (Warning)] Variable "arrayOfHardCodedStrings" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |arrayOfHardCodedStrings| var arrayOfNonRuntimeFunctionCalls = [for i in range(0,3): concat('hi', i)] //@[04:34) [no-unused-vars (Warning)] Variable "arrayOfNonRuntimeFunctionCalls" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |arrayOfNonRuntimeFunctionCalls| //@[59:74) [prefer-interpolation (Warning)] Use string interpolation instead of the concat function. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-interpolation)) |concat('hi', i)| @@ -453,3 +449,17 @@ var prefix = take('food', 3) var isPrefixed = startsWith('food', 'foo') //@[04:14) [no-unused-vars (Warning)] Variable "isPrefixed" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |isPrefixed| +var spread = { + foo: 'abc' + ...issue1332 +} + +var test = { +//@[04:08) [no-unused-vars (Warning)] Variable "test" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |test| + ...spread + bar: 'def' +} + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] +//@[04:15) [no-unused-vars (Warning)] Variable "arraySpread" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |arraySpread| + diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.formatted.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.formatted.bicep index 2582d3f9d8d..71fabbbed2d 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.formatted.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.formatted.bicep @@ -372,3 +372,15 @@ var joinedString = join(['I', 'love', 'Bicep!'], ' ') var prefix = take('food', 3) var isPrefixed = startsWith('food', 'foo') + +var spread = { + foo: 'abc' + ...issue1332 +} + +var test = { + ...spread + bar: 'def' +} + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.ir.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.ir.bicep index e070de35b03..3aa8b09c29a 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.ir.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.ir.bicep @@ -1,5 +1,5 @@ -//@[000:7923) ProgramExpression +//@[000:8106) ProgramExpression // int @sys.description('an int variable') //@[000:0050) ├─DeclaredVariableExpression { Name = myInt } @@ -1079,8 +1079,42 @@ var prefix = take('food', 3) //@[018:0024) | ├─StringLiteralExpression { Value = food } //@[026:0027) | └─IntegerLiteralExpression { Value = 3 } var isPrefixed = startsWith('food', 'foo') -//@[000:0042) └─DeclaredVariableExpression { Name = isPrefixed } -//@[017:0042) └─FunctionCallExpression { Name = startsWith } -//@[028:0034) ├─StringLiteralExpression { Value = food } -//@[036:0041) └─StringLiteralExpression { Value = foo } +//@[000:0042) ├─DeclaredVariableExpression { Name = isPrefixed } +//@[017:0042) | └─FunctionCallExpression { Name = startsWith } +//@[028:0034) | ├─StringLiteralExpression { Value = food } +//@[036:0041) | └─StringLiteralExpression { Value = foo } + +var spread = { +//@[000:0044) ├─DeclaredVariableExpression { Name = spread } +//@[013:0044) | └─FunctionCallExpression { Name = shallowMerge } +//@[013:0044) | └─ArrayExpression +//@[013:0044) | ├─ObjectExpression + foo: 'abc' +//@[002:0012) | | └─ObjectPropertyExpression +//@[002:0005) | | ├─StringLiteralExpression { Value = foo } +//@[007:0012) | | └─StringLiteralExpression { Value = abc } + ...issue1332 +//@[005:0014) | └─VariableReferenceExpression { Variable = issue1332 } +} + +var test = { +//@[000:0039) ├─DeclaredVariableExpression { Name = test } +//@[011:0039) | └─FunctionCallExpression { Name = shallowMerge } +//@[011:0039) | └─ArrayExpression +//@[011:0039) | └─ObjectExpression + ...spread +//@[005:0011) | ├─VariableReferenceExpression { Variable = spread } + bar: 'def' +//@[002:0012) | └─ObjectPropertyExpression +//@[002:0005) | ├─StringLiteralExpression { Value = bar } +//@[007:0012) | └─StringLiteralExpression { Value = def } +} + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] +//@[000:0094) └─DeclaredVariableExpression { Name = arraySpread } +//@[018:0094) └─FunctionCallExpression { Name = flatten } +//@[018:0094) └─ArrayExpression +//@[022:0037) ├─VariableReferenceExpression { Variable = arrayOfBooleans } +//@[042:0065) ├─VariableReferenceExpression { Variable = arrayOfHardCodedNumbers } +//@[070:0093) └─VariableReferenceExpression { Variable = arrayOfHardCodedStrings } diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.json b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.json index 97269da8f48..5bb8d02a380 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.json +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "dev", - "templateHash": "15138089025540421016" + "templateHash": "3772439174458986220" } }, "parameters": { @@ -285,7 +285,10 @@ }, "joinedString": "[join(createArray('I', 'love', 'Bicep!'), ' ')]", "prefix": "[take('food', 3)]", - "isPrefixed": "[startsWith('food', 'foo')]" + "isPrefixed": "[startsWith('food', 'foo')]", + "spread": "[shallowMerge(createArray(createObject('foo', 'abc'), variables('issue1332')))]", + "test": "[shallowMerge(createArray(variables('spread'), createObject('bar', 'def')))]", + "arraySpread": "[flatten(createArray(variables('arrayOfBooleans'), variables('arrayOfHardCodedNumbers'), variables('arrayOfHardCodedStrings')))]" }, "resources": [] } \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.sourcemap.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.sourcemap.bicep index 0a1b824950e..219cd697791 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.sourcemap.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.sourcemap.bicep @@ -630,5 +630,20 @@ var joinedString = join(['I', 'love', 'Bicep!'], ' ') var prefix = take('food', 3) //@ "prefix": "[take('food', 3)]", var isPrefixed = startsWith('food', 'foo') -//@ "isPrefixed": "[startsWith('food', 'foo')]" +//@ "isPrefixed": "[startsWith('food', 'foo')]", + +var spread = { +//@ "spread": "[shallowMerge(createArray(createObject('foo', 'abc'), variables('issue1332')))]", + foo: 'abc' + ...issue1332 +} + +var test = { +//@ "test": "[shallowMerge(createArray(variables('spread'), createObject('bar', 'def')))]", + ...spread + bar: 'def' +} + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] +//@ "arraySpread": "[flatten(createArray(variables('arrayOfBooleans'), variables('arrayOfHardCodedNumbers'), variables('arrayOfHardCodedStrings')))]" diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbolicnames.json b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbolicnames.json index 35c88874eaa..da71d175ea3 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbolicnames.json +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbolicnames.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "dev", - "templateHash": "10440735979787247431" + "templateHash": "4260162552164475821" } }, "parameters": { @@ -286,7 +286,10 @@ }, "joinedString": "[join(createArray('I', 'love', 'Bicep!'), ' ')]", "prefix": "[take('food', 3)]", - "isPrefixed": "[startsWith('food', 'foo')]" + "isPrefixed": "[startsWith('food', 'foo')]", + "spread": "[shallowMerge(createArray(createObject('foo', 'abc'), variables('issue1332')))]", + "test": "[shallowMerge(createArray(variables('spread'), createObject('bar', 'def')))]", + "arraySpread": "[flatten(createArray(variables('arrayOfBooleans'), variables('arrayOfHardCodedNumbers'), variables('arrayOfHardCodedStrings')))]" }, "resources": {} } \ No newline at end of file diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbols.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbols.bicep index 46e0f9ca183..9a2cd8f8390 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbols.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.symbols.bicep @@ -493,3 +493,18 @@ var prefix = take('food', 3) var isPrefixed = startsWith('food', 'foo') //@[04:14) Variable isPrefixed. Type: true. Declaration start char: 0, length: 42 +var spread = { +//@[04:10) Variable spread. Type: object. Declaration start char: 0, length: 44 + foo: 'abc' + ...issue1332 +} + +var test = { +//@[04:08) Variable test. Type: object. Declaration start char: 0, length: 39 + ...spread + bar: 'def' +} + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] +//@[04:15) Variable arraySpread. Type: ('hi' | 3 | bool)[]. Declaration start char: 0, length: 94 + diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.syntax.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.syntax.bicep index f1fb9213a4a..927ed7c8feb 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.syntax.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.syntax.bicep @@ -1,5 +1,5 @@ -//@[000:7923) ProgramSyntax +//@[000:8106) ProgramSyntax //@[000:0001) ├─Token(NewLine) |\n| // int //@[006:0007) ├─Token(NewLine) |\n| @@ -3167,6 +3167,90 @@ var isPrefixed = startsWith('food', 'foo') //@[036:0041) | | └─StringSyntax //@[036:0041) | | └─Token(StringComplete) |'foo'| //@[041:0042) | └─Token(RightParen) |)| -//@[042:0043) ├─Token(NewLine) |\n| +//@[042:0044) ├─Token(NewLine) |\n\n| + +var spread = { +//@[000:0044) ├─VariableDeclarationSyntax +//@[000:0003) | ├─Token(Identifier) |var| +//@[004:0010) | ├─IdentifierSyntax +//@[004:0010) | | └─Token(Identifier) |spread| +//@[011:0012) | ├─Token(Assignment) |=| +//@[013:0044) | └─ObjectSyntax +//@[013:0014) | ├─Token(LeftBrace) |{| +//@[014:0015) | ├─Token(NewLine) |\n| + foo: 'abc' +//@[002:0012) | ├─ObjectPropertySyntax +//@[002:0005) | | ├─IdentifierSyntax +//@[002:0005) | | | └─Token(Identifier) |foo| +//@[005:0006) | | ├─Token(Colon) |:| +//@[007:0012) | | └─StringSyntax +//@[007:0012) | | └─Token(StringComplete) |'abc'| +//@[012:0013) | ├─Token(NewLine) |\n| + ...issue1332 +//@[002:0014) | ├─SpreadExpressionSyntax +//@[002:0005) | | ├─Token(Ellipsis) |...| +//@[005:0014) | | └─VariableAccessSyntax +//@[005:0014) | | └─IdentifierSyntax +//@[005:0014) | | └─Token(Identifier) |issue1332| +//@[014:0015) | ├─Token(NewLine) |\n| +} +//@[000:0001) | └─Token(RightBrace) |}| +//@[001:0003) ├─Token(NewLine) |\n\n| + +var test = { +//@[000:0039) ├─VariableDeclarationSyntax +//@[000:0003) | ├─Token(Identifier) |var| +//@[004:0008) | ├─IdentifierSyntax +//@[004:0008) | | └─Token(Identifier) |test| +//@[009:0010) | ├─Token(Assignment) |=| +//@[011:0039) | └─ObjectSyntax +//@[011:0012) | ├─Token(LeftBrace) |{| +//@[012:0013) | ├─Token(NewLine) |\n| + ...spread +//@[002:0011) | ├─SpreadExpressionSyntax +//@[002:0005) | | ├─Token(Ellipsis) |...| +//@[005:0011) | | └─VariableAccessSyntax +//@[005:0011) | | └─IdentifierSyntax +//@[005:0011) | | └─Token(Identifier) |spread| +//@[011:0012) | ├─Token(NewLine) |\n| + bar: 'def' +//@[002:0012) | ├─ObjectPropertySyntax +//@[002:0005) | | ├─IdentifierSyntax +//@[002:0005) | | | └─Token(Identifier) |bar| +//@[005:0006) | | ├─Token(Colon) |:| +//@[007:0012) | | └─StringSyntax +//@[007:0012) | | └─Token(StringComplete) |'def'| +//@[012:0013) | ├─Token(NewLine) |\n| +} +//@[000:0001) | └─Token(RightBrace) |}| +//@[001:0003) ├─Token(NewLine) |\n\n| + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] +//@[000:0094) ├─VariableDeclarationSyntax +//@[000:0003) | ├─Token(Identifier) |var| +//@[004:0015) | ├─IdentifierSyntax +//@[004:0015) | | └─Token(Identifier) |arraySpread| +//@[016:0017) | ├─Token(Assignment) |=| +//@[018:0094) | └─ArraySyntax +//@[018:0019) | ├─Token(LeftSquare) |[| +//@[019:0037) | ├─SpreadExpressionSyntax +//@[019:0022) | | ├─Token(Ellipsis) |...| +//@[022:0037) | | └─VariableAccessSyntax +//@[022:0037) | | └─IdentifierSyntax +//@[022:0037) | | └─Token(Identifier) |arrayOfBooleans| +//@[037:0038) | ├─Token(Comma) |,| +//@[039:0065) | ├─SpreadExpressionSyntax +//@[039:0042) | | ├─Token(Ellipsis) |...| +//@[042:0065) | | └─VariableAccessSyntax +//@[042:0065) | | └─IdentifierSyntax +//@[042:0065) | | └─Token(Identifier) |arrayOfHardCodedNumbers| +//@[065:0066) | ├─Token(Comma) |,| +//@[067:0093) | ├─SpreadExpressionSyntax +//@[067:0070) | | ├─Token(Ellipsis) |...| +//@[070:0093) | | └─VariableAccessSyntax +//@[070:0093) | | └─IdentifierSyntax +//@[070:0093) | | └─Token(Identifier) |arrayOfHardCodedStrings| +//@[093:0094) | └─Token(RightSquare) |]| +//@[094:0095) ├─Token(NewLine) |\n| //@[000:0000) └─Token(EndOfFile) || diff --git a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.tokens.bicep b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.tokens.bicep index ce4b0a49d46..bf258ab1f94 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.tokens.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Variables_LF/main.tokens.bicep @@ -1992,6 +1992,60 @@ var isPrefixed = startsWith('food', 'foo') //@[034:035) Comma |,| //@[036:041) StringComplete |'foo'| //@[041:042) RightParen |)| -//@[042:043) NewLine |\n| +//@[042:044) NewLine |\n\n| + +var spread = { +//@[000:003) Identifier |var| +//@[004:010) Identifier |spread| +//@[011:012) Assignment |=| +//@[013:014) LeftBrace |{| +//@[014:015) NewLine |\n| + foo: 'abc' +//@[002:005) Identifier |foo| +//@[005:006) Colon |:| +//@[007:012) StringComplete |'abc'| +//@[012:013) NewLine |\n| + ...issue1332 +//@[002:005) Ellipsis |...| +//@[005:014) Identifier |issue1332| +//@[014:015) NewLine |\n| +} +//@[000:001) RightBrace |}| +//@[001:003) NewLine |\n\n| + +var test = { +//@[000:003) Identifier |var| +//@[004:008) Identifier |test| +//@[009:010) Assignment |=| +//@[011:012) LeftBrace |{| +//@[012:013) NewLine |\n| + ...spread +//@[002:005) Ellipsis |...| +//@[005:011) Identifier |spread| +//@[011:012) NewLine |\n| + bar: 'def' +//@[002:005) Identifier |bar| +//@[005:006) Colon |:| +//@[007:012) StringComplete |'def'| +//@[012:013) NewLine |\n| +} +//@[000:001) RightBrace |}| +//@[001:003) NewLine |\n\n| + +var arraySpread = [...arrayOfBooleans, ...arrayOfHardCodedNumbers, ...arrayOfHardCodedStrings] +//@[000:003) Identifier |var| +//@[004:015) Identifier |arraySpread| +//@[016:017) Assignment |=| +//@[018:019) LeftSquare |[| +//@[019:022) Ellipsis |...| +//@[022:037) Identifier |arrayOfBooleans| +//@[037:038) Comma |,| +//@[039:042) Ellipsis |...| +//@[042:065) Identifier |arrayOfHardCodedNumbers| +//@[065:066) Comma |,| +//@[067:070) Ellipsis |...| +//@[070:093) Identifier |arrayOfHardCodedStrings| +//@[093:094) RightSquare |]| +//@[094:095) NewLine |\n| //@[000:000) EndOfFile || diff --git a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.diagnostics.bicepparam b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.diagnostics.bicepparam index 223ff2f6b19..eca79224bec 100644 --- a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.diagnostics.bicepparam +++ b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.diagnostics.bicepparam @@ -31,6 +31,7 @@ param testEndsWith = endsWith('foo', []) param testFilter = filter([1, 2], i => i < 'foo') //@[19:49) [BCP033 (Error)] Expected a value of type "object" but the provided value is of type "(1 | 2)[]". (CodeDescription: none) |filter([1, 2], i => i < 'foo')| //@[19:49) [BCP338 (Error)] Failed to evaluate parameter "testFilter": The template language function 'less' expects two parameters of matching types. The function was invoked with values of type 'Integer' and 'String' that do not match. (CodeDescription: none) |filter([1, 2], i => i < 'foo')| +//@[39:48) [BCP045 (Error)] Cannot apply operator "<" to operands of type "1 | 2" and "'foo'". (CodeDescription: none) |i < 'foo'| param testFirst = first('asdfds') //@[18:33) [BCP033 (Error)] Expected a value of type "object" but the provided value is of type "'a'". (CodeDescription: none) |first('asdfds')| param testFlatten = flatten({foo: 'bar'}) @@ -83,7 +84,9 @@ param testReplace = replace('abc', 'b', {}) param testSkip = skip([1, 2, 3], '1') //@[33:36) [BCP070 (Error)] Argument of type "'1'" is not assignable to parameter of type "int". (CodeDescription: none) |'1'| param testSort = sort(['c', 'd', 'a'], (a, b) => a + b) -//@[39:54) [BCP070 (Error)] Argument of type "(any, any) => int" is not assignable to parameter of type "(any, any) => bool". (CodeDescription: none) |(a, b) => a + b| +//@[17:55) [BCP033 (Error)] Expected a value of type "object" but the provided value is of type "('a' | 'c' | 'd')[]". (CodeDescription: none) |sort(['c', 'd', 'a'], (a, b) => a + b)| +//@[17:55) [BCP338 (Error)] Failed to evaluate parameter "testSort": Unhandled exception during evaluating template language function 'sort' is not handled. (CodeDescription: none) |sort(['c', 'd', 'a'], (a, b) => a + b)| +//@[49:54) [BCP045 (Error)] Cannot apply operator "+" to operands of type "'a' | 'c' | 'd'" and "'a' | 'c' | 'd'". Use string interpolation instead. (CodeDescription: none) |a + b| param testSplit = split('a/b/c', 1 + 2) //@[33:38) [BCP070 (Error)] Argument of type "3" is not assignable to parameter of type "array | string". (CodeDescription: none) |1 + 2| param testStartsWith = startsWith('abc', {}) @@ -97,7 +100,7 @@ param testTake = take([1, 2, 3], '2') param testToLower = toLower(123) //@[28:31) [BCP070 (Error)] Argument of type "123" is not assignable to parameter of type "string". (CodeDescription: none) |123| param testToObject = toObject(['a', 'b', 'c'], x => {x: x}, x => 'Hi ${x}!') -//@[47:58) [BCP070 (Error)] Argument of type "any => object" is not assignable to parameter of type "any => string". (CodeDescription: none) |x => {x: x}| +//@[47:58) [BCP070 (Error)] Argument of type "('a' | 'b' | 'c') => object" is not assignable to parameter of type "any => string". (CodeDescription: none) |x => {x: x}| param testToUpper = toUpper([123]) //@[28:33) [BCP070 (Error)] Argument of type "[123]" is not assignable to parameter of type "string". (CodeDescription: none) |[123]| param testTrim = trim(123) diff --git a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.symbols.bicepparam b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.symbols.bicepparam index e00b9bb724e..fbf9c193a49 100644 --- a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.symbols.bicepparam +++ b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Expressions/parameters.symbols.bicepparam @@ -73,7 +73,7 @@ param testReplace = replace('abc', 'b', {}) param testSkip = skip([1, 2, 3], '1') //@[6:14) ParameterAssignment testSkip. Type: error. Declaration start char: 0, length: 37 param testSort = sort(['c', 'd', 'a'], (a, b) => a + b) -//@[6:14) ParameterAssignment testSort. Type: error. Declaration start char: 0, length: 55 +//@[6:14) ParameterAssignment testSort. Type: ('a' | 'c' | 'd')[]. Declaration start char: 0, length: 55 param testSplit = split('a/b/c', 1 + 2) //@[6:15) ParameterAssignment testSplit. Type: error. Declaration start char: 0, length: 39 param testStartsWith = startsWith('abc', {}) diff --git a/src/Bicep.Core.UnitTests/Diagnostics/ErrorBuilderTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/ErrorBuilderTests.cs index a9662748653..4bc6abd0c2a 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/ErrorBuilderTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/ErrorBuilderTests.cs @@ -193,6 +193,13 @@ private static object CreateMockParameter(ParameterInfo parameter, int index) asClause: SyntaxFactory.EmptySkippedTrivia); } + if (parameter.ParameterType == typeof(SpreadExpressionSyntax)) + { + return new SpreadExpressionSyntax( + SyntaxFactory.EllipsisToken, + TestSyntaxFactory.CreateVariableAccess("identifier")); + } + if (parameter.ParameterType == typeof(ExportMetadataKind)) { return ExportMetadataKind.Error; diff --git a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs index cf8c5c06844..5c5ae698a43 100644 --- a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs +++ b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs @@ -2166,6 +2166,16 @@ public FixableDiagnostic LegacyProviderSpecificationIsDeprecated(LegacyProviderS DiagnosticStyling.Default, codeFix); } + + public ErrorDiagnostic SpreadOperatorUnsupportedInLocation(SpreadExpressionSyntax spread) => new( + TextSpan, + "BCP396", + $"The spread operator \"{spread.Ellipsis.Text}\" is not permitted in this location."); + + public ErrorDiagnostic SpreadOperatorRequiresAssignableValue(SpreadExpressionSyntax spread, TypeSymbol requiredType) => new( + TextSpan, + "BCP397", + $"The spread operator \"{spread.Ellipsis.Text}\" can only be used in this context for an expression assignable to type \"{requiredType}\"."); } public static DiagnosticBuilderInternal ForPosition(TextSpan span) diff --git a/src/Bicep.Core/Emit/EmitLimitationCalculator.cs b/src/Bicep.Core/Emit/EmitLimitationCalculator.cs index 1ca90630f75..fe9f1f682bd 100644 --- a/src/Bicep.Core/Emit/EmitLimitationCalculator.cs +++ b/src/Bicep.Core/Emit/EmitLimitationCalculator.cs @@ -53,6 +53,7 @@ public static EmitLimitationInfo Calculate(SemanticModel model) BlockAssertsWithoutExperimentalFeatures(model, diagnostics); BlockNamesDistinguishedOnlyByCase(model, diagnostics); BlockResourceDerivedTypesThatDoNotDereferenceProperties(model, diagnostics); + BlockSpreadInUnsupportedLocations(model, diagnostics); var paramAssignments = CalculateParameterAssignments(model, diagnostics); @@ -682,5 +683,40 @@ TypeAdditionalPropertiesAccessSyntax or !IsPermittedResourceDerivedTypeParent(model.Binder, model.Binder.GetParent(typeInstantiation))) .Select(typeInstantiaion => DiagnosticBuilder.ForPosition(typeInstantiaion).CannotUseEntireResourceBodyAsType())); } + + private static void BlockSpreadInUnsupportedLocations(SemanticModel model, IDiagnosticWriter diagnostics) + { + IEnumerable getObjectSyntaxesToBlock() + { + foreach (var module in model.Root.ModuleDeclarations) + { + if (module.DeclaringModule.TryGetBody() is {} body) + { + yield return body; + + if (body.TryGetPropertyByName(LanguageConstants.ModuleParamsPropertyName)?.Value is ObjectSyntax paramsBody) + { + yield return paramsBody; + } + } + } + + foreach (var resource in model.Root.ResourceDeclarations) + { + if (resource.DeclaringResource.TryGetBody() is {} body) + { + yield return body; + } + } + } + + foreach (var body in getObjectSyntaxesToBlock()) + { + foreach (var spread in body.Children.OfType()) + { + diagnostics.Write(spread, x => x.SpreadOperatorUnsupportedInLocation(spread)); + } + } + } } } diff --git a/src/Bicep.Core/Intermediate/ExpressionBuilder.cs b/src/Bicep.Core/Intermediate/ExpressionBuilder.cs index 5d7246e8e0f..b3d2f4b23ff 100644 --- a/src/Bicep.Core/Intermediate/ExpressionBuilder.cs +++ b/src/Bicep.Core/Intermediate/ExpressionBuilder.cs @@ -101,8 +101,7 @@ private Expression ConvertWithoutLowering(SyntaxBase syntax) case ObjectSyntax @object: return ConvertObject(@object); case ArraySyntax array: - var items = array.Items.Select(x => ConvertWithoutLowering(x.Value)); - return new ArrayExpression(array, items.ToImmutableArray()); + return ConvertArray(array); case TernaryOperationSyntax ternary: return new TernaryExpression( ternary, @@ -741,10 +740,81 @@ void AddLoop(LoopExpressionContext newLoop) dependencies); } - private ObjectExpression ConvertObject(ObjectSyntax @object) - => new( - @object, - @object.Properties.Select(ConvertObjectProperty).ToImmutableArray()); + private Expression ConvertArray(ArraySyntax array) + { + var hasSpread = false; + var chunks = new List(); + var currentChunk = new List(); + void completePreviousChunk() + { + if (currentChunk.Count > 0) + { + chunks.Add(new ArrayExpression(array, currentChunk.ToImmutableArray())); + currentChunk.Clear(); + } + } + + foreach (var child in array.Children) + { + if (child is SpreadExpressionSyntax spread) + { + completePreviousChunk(); + chunks.Add(ConvertWithoutLowering(spread.Expression)); + hasSpread = true; + } + else if (child is ArrayItemSyntax arrayItem) + { + currentChunk.Add(ConvertWithoutLowering(arrayItem.Value)); + } + } + completePreviousChunk(); + + return chunks.Count switch + { + 0 => new ArrayExpression(array, []), + // preserve [ ...[ bar ] ] rather than converting it to [ foo: bar ] + 1 when !hasSpread => chunks[0], + _ => new FunctionCallExpression(array, "flatten", [new ArrayExpression(array, chunks.ToImmutableArray())]), + }; + } + + private Expression ConvertObject(ObjectSyntax @object) + { + var hasSpread = false; + var chunks = new List(); + var currentChunk = new List(); + void completePreviousChunk() + { + if (currentChunk.Count > 0) + { + chunks.Add(new ObjectExpression(@object, currentChunk.ToImmutableArray())); + currentChunk.Clear(); + } + } + + foreach (var child in @object.Children) + { + if (child is SpreadExpressionSyntax spread) + { + completePreviousChunk(); + chunks.Add(ConvertWithoutLowering(spread.Expression)); + hasSpread = true; + } + else if (child is ObjectPropertySyntax objectProperty) + { + currentChunk.Add(ConvertObjectProperty(objectProperty)); + } + } + completePreviousChunk(); + + return chunks.Count switch + { + 0 => new ObjectExpression(@object, []), + // preserve { ...{ foo: bar } } rather than converting it to { foo: bar } + 1 when !hasSpread => chunks[0], + _ => new FunctionCallExpression(@object, "shallowMerge", [new ArrayExpression(@object, chunks.ToImmutableArray())]), + }; + } private ObjectPropertyExpression ConvertObjectProperty(ObjectPropertySyntax syntax) { diff --git a/src/Bicep.Core/Parsing/BaseParser.cs b/src/Bicep.Core/Parsing/BaseParser.cs index 5b9fdff37a2..274549ae10a 100644 --- a/src/Bicep.Core/Parsing/BaseParser.cs +++ b/src/Bicep.Core/Parsing/BaseParser.cs @@ -207,17 +207,24 @@ private SyntaxBase Array() var itemsOrTokens = HandleArrayOrObjectElements( closingTokenType: TokenType.RightSquare, - parseChildElement: ArrayItem); + parseChildElement: ArrayElement); var closeBracket = Expect(TokenType.RightSquare, b => b.ExpectedCharacter("]")); return new ArraySyntax(openBracket, itemsOrTokens, closeBracket); } - private SyntaxBase ArrayItem() + private SyntaxBase ArrayElement() { return this.WithRecovery(() => { + var current = this.reader.Peek(); + + if (current.Type == TokenType.Ellipsis) + { + return SpreadExpression([TokenType.NewLine, TokenType.RightSquare]); + } + var value = this.Expression(ExpressionFlags.AllowComplexLiterals); return new ArrayItemSyntax(value); @@ -1057,14 +1064,22 @@ protected ObjectSyntax Object(ExpressionFlags expressionFlags) var itemsOrTokens = HandleArrayOrObjectElements( closingTokenType: TokenType.RightBrace, - parseChildElement: () => ObjectProperty(expressionFlags)); + parseChildElement: () => ObjectElement(expressionFlags)); var closeBrace = Expect(TokenType.RightBrace, b => b.ExpectedCharacter("}")); return new ObjectSyntax(openBrace, itemsOrTokens, closeBrace); } - private SyntaxBase ObjectProperty(ExpressionFlags expressionFlags) + private SpreadExpressionSyntax SpreadExpression(TokenType[] terminatingTypes) + { + var ellipsis = this.Expect(TokenType.Ellipsis, b => b.ExpectedCharacter("...")); + var expression = this.WithRecovery(() => this.Expression(ExpressionFlags.AllowComplexLiterals), GetSuppressionFlag(ellipsis), terminatingTypes); + + return new SpreadExpressionSyntax(ellipsis, expression); + } + + private SyntaxBase ObjectElement(ExpressionFlags expressionFlags) { return this.WithRecovery(() => { @@ -1087,6 +1102,11 @@ private SyntaxBase ObjectProperty(ExpressionFlags expressionFlags) return this.Declaration(LanguageConstants.ResourceKeyword); } + if (current.Type == TokenType.Ellipsis) + { + return SpreadExpression([TokenType.NewLine, TokenType.RightBrace]); + } + var key = this.WithRecovery( () => ThrowIfSkipped( () => diff --git a/src/Bicep.Core/Parsing/Lexer.cs b/src/Bicep.Core/Parsing/Lexer.cs index 0b549b10df6..885cdb2426d 100644 --- a/src/Bicep.Core/Parsing/Lexer.cs +++ b/src/Bicep.Core/Parsing/Lexer.cs @@ -900,6 +900,12 @@ private TokenType ScanToken() case ',': return TokenType.Comma; case '.': + switch (textWindow.Peek(), textWindow.Peek(1)) + { + case ('.', '.'): + textWindow.Advance(2); + return TokenType.Ellipsis; + } return TokenType.Dot; case '?': if (!textWindow.IsAtEnd()) diff --git a/src/Bicep.Core/Parsing/TokenType.cs b/src/Bicep.Core/Parsing/TokenType.cs index 9212a3cae7c..1b9c6e6e9fa 100644 --- a/src/Bicep.Core/Parsing/TokenType.cs +++ b/src/Bicep.Core/Parsing/TokenType.cs @@ -52,5 +52,6 @@ public enum TokenType Pipe, WithKeyword, AsKeyword, + Ellipsis, } } diff --git a/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.SyntaxVisitor.cs b/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.SyntaxVisitor.cs index 18205463c8e..6b57d59c62b 100644 --- a/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.SyntaxVisitor.cs +++ b/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.SyntaxVisitor.cs @@ -174,6 +174,8 @@ public void VisitTypeAdditionalPropertiesAccessSyntax(TypeAdditionalPropertiesAc public void VisitTypeItemsAccessSyntax(TypeItemsAccessSyntax syntax) => this.Apply(syntax, LayoutTypeItemsAccessSyntax); + public void VisitSpreadExpressionSyntax(SpreadExpressionSyntax syntax) => this.Apply(syntax, LayoutSpreadExpressionSyntax); + public IEnumerable Layout(SyntaxBase syntax) { syntax.Accept(this); diff --git a/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.cs b/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.cs index f81af416272..6420f5e7827 100644 --- a/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.cs +++ b/src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.cs @@ -603,6 +603,9 @@ private IEnumerable LayoutInstanceParameterizedTypeInstantiationSyntax private IEnumerable LayoutTypeItemsAccessSyntax(TypeItemsAccessSyntax syntax) => this.Glue(syntax.BaseExpression, syntax.OpenSquare, syntax.Asterisk, syntax.CloseSquare); + private IEnumerable LayoutSpreadExpressionSyntax(SpreadExpressionSyntax syntax) => + this.Glue(syntax.Ellipsis, syntax.Expression); + private IEnumerable LayoutLeadingNodes(IEnumerable leadingNodes) => this.LayoutMany(leadingNodes) .Where(x => x != HardLine); // Remove empty lines between decorators. diff --git a/src/Bicep.Core/Rewriters/DependsOnRemovalRewriter.cs b/src/Bicep.Core/Rewriters/DependsOnRemovalRewriter.cs index b17bf76a357..9ded00879b1 100644 --- a/src/Bicep.Core/Rewriters/DependsOnRemovalRewriter.cs +++ b/src/Bicep.Core/Rewriters/DependsOnRemovalRewriter.cs @@ -59,14 +59,14 @@ value switch } var builtInDependencies = new HashSet(); - foreach (var property in @object.Properties) + foreach (var child in @object.Children) { - if (property == dependsOnProperty) + if (child == dependsOnProperty) { continue; } - var dependencies = ResourceDependencyFinderVisitor.GetResourceDependencies(semanticModel, property); + var dependencies = ResourceDependencyFinderVisitor.GetResourceDependencies(semanticModel, child); builtInDependencies.UnionWith(dependencies); } diff --git a/src/Bicep.Core/Syntax/AstVisitor.cs b/src/Bicep.Core/Syntax/AstVisitor.cs index 6318b94c2fc..6753d6c24ed 100644 --- a/src/Bicep.Core/Syntax/AstVisitor.cs +++ b/src/Bicep.Core/Syntax/AstVisitor.cs @@ -454,5 +454,10 @@ public override void VisitTypeItemsAccessSyntax(TypeItemsAccessSyntax syntax) { this.Visit(syntax.BaseExpression); } + + public override void VisitSpreadExpressionSyntax(SpreadExpressionSyntax syntax) + { + this.Visit(syntax.Expression); + } } } diff --git a/src/Bicep.Core/Syntax/CstVisitor.cs b/src/Bicep.Core/Syntax/CstVisitor.cs index 476157c4e9b..567d8c1a72b 100644 --- a/src/Bicep.Core/Syntax/CstVisitor.cs +++ b/src/Bicep.Core/Syntax/CstVisitor.cs @@ -569,5 +569,11 @@ public override void VisitTypeItemsAccessSyntax(TypeItemsAccessSyntax syntax) this.Visit(syntax.Asterisk); this.Visit(syntax.CloseSquare); } + + public override void VisitSpreadExpressionSyntax(SpreadExpressionSyntax syntax) + { + this.Visit(syntax.Ellipsis); + this.Visit(syntax.Expression); + } } } diff --git a/src/Bicep.Core/Syntax/ISyntaxVisitor.cs b/src/Bicep.Core/Syntax/ISyntaxVisitor.cs index ee8b458dd9c..ac4391e7d16 100644 --- a/src/Bicep.Core/Syntax/ISyntaxVisitor.cs +++ b/src/Bicep.Core/Syntax/ISyntaxVisitor.cs @@ -155,5 +155,7 @@ public interface ISyntaxVisitor void VisitTypeArrayAccessSyntax(TypeArrayAccessSyntax syntax); void VisitTypeItemsAccessSyntax(TypeItemsAccessSyntax syntax); + + void VisitSpreadExpressionSyntax(SpreadExpressionSyntax syntax); } } diff --git a/src/Bicep.Core/Syntax/ObjectSyntaxExtensions.cs b/src/Bicep.Core/Syntax/ObjectSyntaxExtensions.cs index 09830a7eb3d..a63441eb86e 100644 --- a/src/Bicep.Core/Syntax/ObjectSyntaxExtensions.cs +++ b/src/Bicep.Core/Syntax/ObjectSyntaxExtensions.cs @@ -29,13 +29,6 @@ public static class ObjectSyntaxExtensions return dictionary.ToImmutableDictionary(LanguageConstants.IdentifierComparer); } - /// - /// Converts a syntactically valid object syntax node to a dictionary mapping property name strings to property syntax node values. Returns the first property value in the case of duplicate names. - /// - /// The object syntax node - public static ImmutableDictionary ToNamedPropertyValueDictionary(this ObjectSyntax syntax) - => ToNamedPropertyDictionary(syntax).ToImmutableDictionary(x => x.Key, x => x.Value.Value, LanguageConstants.IdentifierComparer); - /// /// Returns the specified property by name on any valid or invalid object syntax node if there is exactly one property by that name. /// Returns null if the property does not exist or if multiple properties by that name exist. This method is intended for a single diff --git a/src/Bicep.Core/Syntax/SpreadExpressionSyntax.cs b/src/Bicep.Core/Syntax/SpreadExpressionSyntax.cs new file mode 100644 index 00000000000..d104ad76a77 --- /dev/null +++ b/src/Bicep.Core/Syntax/SpreadExpressionSyntax.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using Bicep.Core.Parsing; + +namespace Bicep.Core.Syntax; + +public class SpreadExpressionSyntax : ExpressionSyntax +{ + public SpreadExpressionSyntax(Token ellipsis, SyntaxBase expression) + { + AssertTokenType(ellipsis, nameof(ellipsis), TokenType.Ellipsis); + + this.Ellipsis = ellipsis; + this.Expression = expression; + } + + public Token Ellipsis { get; } + + public SyntaxBase Expression { get; } + + public override void Accept(ISyntaxVisitor visitor) => visitor.VisitSpreadExpressionSyntax(this); + + public override TextSpan Span => TextSpan.Between(this.Ellipsis, this.Expression); +} \ No newline at end of file diff --git a/src/Bicep.Core/Syntax/SyntaxFactory.cs b/src/Bicep.Core/Syntax/SyntaxFactory.cs index b1ee5d90596..69cca9b3995 100644 --- a/src/Bicep.Core/Syntax/SyntaxFactory.cs +++ b/src/Bicep.Core/Syntax/SyntaxFactory.cs @@ -88,6 +88,7 @@ public static Token GetCommaToken(IEnumerable? leadingTrivia = nul public static Token NullKeywordToken => CreateToken(TokenType.NullKeyword); public static Token ArrowToken => CreateToken(TokenType.Arrow); public static Token EndOfFileToken => CreateToken(TokenType.EndOfFile); + public static Token EllipsisToken => CreateToken(TokenType.Ellipsis); public static Token TargetScopeKeywordToken => CreateIdentifierTokenWithTrailingSpace(LanguageConstants.TargetScopeKeyword); public static Token ImportKeywordToken => CreateIdentifierTokenWithTrailingSpace(LanguageConstants.ImportKeyword); diff --git a/src/Bicep.Core/Syntax/SyntaxFacts.cs b/src/Bicep.Core/Syntax/SyntaxFacts.cs index 856facef425..c03a37f6c09 100644 --- a/src/Bicep.Core/Syntax/SyntaxFacts.cs +++ b/src/Bicep.Core/Syntax/SyntaxFacts.cs @@ -62,6 +62,7 @@ TokenType.MultilineString or TokenType.Pipe => "|", TokenType.WithKeyword => "with", TokenType.AsKeyword => "as", + TokenType.Ellipsis => "...", _ => null, }; diff --git a/src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs b/src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs index 06dcc765963..50b08469dad 100644 --- a/src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs +++ b/src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs @@ -1252,5 +1252,19 @@ protected virtual SyntaxBase ReplaceParameterizedTypeArgumentSyntax(Parameterize return new ParameterizedTypeArgumentSyntax(expression); } void ISyntaxVisitor.VisitParameterizedTypeArgumentSyntax(ParameterizedTypeArgumentSyntax syntax) => ReplaceCurrent(syntax, ReplaceParameterizedTypeArgumentSyntax); + + protected virtual SyntaxBase ReplaceSpreadExpressionSyntax(SpreadExpressionSyntax syntax) + { + var hasChanges = TryRewriteStrict(syntax.Ellipsis, out var ellipsis); + hasChanges |= TryRewriteStrict(syntax.Expression, out var expression); + + if (!hasChanges) + { + return syntax; + } + + return new SpreadExpressionSyntax(ellipsis, expression); + } + void ISyntaxVisitor.VisitSpreadExpressionSyntax(SpreadExpressionSyntax syntax) => ReplaceCurrent(syntax, ReplaceSpreadExpressionSyntax); } } diff --git a/src/Bicep.Core/Syntax/SyntaxVisitor.cs b/src/Bicep.Core/Syntax/SyntaxVisitor.cs index ca47ad1b238..e87bb3fdcdc 100644 --- a/src/Bicep.Core/Syntax/SyntaxVisitor.cs +++ b/src/Bicep.Core/Syntax/SyntaxVisitor.cs @@ -160,6 +160,8 @@ public abstract class SyntaxVisitor : ISyntaxVisitor public abstract void VisitTypeItemsAccessSyntax(TypeItemsAccessSyntax syntax); + public abstract void VisitSpreadExpressionSyntax(SpreadExpressionSyntax syntax); + public void Visit(SyntaxBase? node) { if (node == null) diff --git a/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs b/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs index 5e621c3674b..6abb47177ce 100644 --- a/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs +++ b/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Immutable; +using Azure.Deployments.Templates.Export; using Bicep.Core.Diagnostics; using Bicep.Core.Emit; using Bicep.Core.Extensions; @@ -1187,15 +1188,32 @@ public override void VisitObjectSyntax(ObjectSyntax syntax) } } - var propertyTypes = new List(); - foreach (var objectProperty in syntax.Properties) + var childTypes = new List(); + foreach (var child in syntax.Children) { - var propertyType = typeManager.GetTypeInfo(objectProperty); - CollectErrors(errors, propertyType); - propertyTypes.Add(propertyType); + var childType = child switch { + ObjectPropertySyntax x => typeManager.GetTypeInfo(x), + SpreadExpressionSyntax x => typeManager.GetTypeInfo(x.Expression), + _ => null, + }; + + if (childType is null) + { + continue; + } + + if (child is SpreadExpressionSyntax spread && + childType is not ErrorType && + !TypeValidator.AreTypesAssignable(childType, LanguageConstants.Object)) + { + childType = ErrorType.Create(DiagnosticBuilder.ForPosition(child).SpreadOperatorRequiresAssignableValue(spread, LanguageConstants.Object)); + } + + CollectErrors(errors, childType); + childTypes.Add(childType); } - if (PropagateErrorType(errors, propertyTypes)) + if (PropagateErrorType(errors, childTypes)) { return ErrorType.Create(errors); } @@ -1203,33 +1221,61 @@ public override void VisitObjectSyntax(ObjectSyntax syntax) // Discriminated objects should have been resolved by the declared type manager. var declaredType = typeManager.GetDeclaredType(syntax); - // type results are cached - var namedProperties = syntax.Properties - .GroupByExcludingNull(p => p.TryGetKeyText(), LanguageConstants.IdentifierComparer) - .Select(group => + var namedProperties = new Dictionary(LanguageConstants.IdentifierComparer); + var additionalProperties = new List(); + foreach (var child in syntax.Children) + { + if (child is ObjectPropertySyntax propertySyntax) { - var resolvedType = TypeHelper.CreateTypeUnion(group.Select(p => typeManager.GetTypeInfo(p))); + var resolvedType = typeManager.GetTypeInfo(propertySyntax); - if (declaredType is ObjectType objectType && objectType.Properties.TryGetValue(group.Key, out var property)) + if (propertySyntax.TryGetKeyText() is {} name) { - // we've found a declared object type for the containing object, with a matching property name definition. - // preserve the type property details (name, descriptions etc.), and update the assigned type. - return new TypeProperty(property.Name, resolvedType, property.Flags, property.Description); + if (declaredType is ObjectType objectType && objectType.Properties.TryGetValue(name, out var property)) + { + // we've found a declared object type for the containing object, with a matching property name definition. + // preserve the type property details (name, descriptions etc.), and update the assigned type. + namedProperties[name] = new TypeProperty(property.Name, resolvedType, property.Flags, property.Description); + } + else + { + // we've not been able to find a declared object type for the containing object, or it doesn't contain a property matching this one. + // best we can do is to simply generate a property for the assigned type. + namedProperties[name] = new TypeProperty(name, resolvedType, TypePropertyFlags.Required); + } } + else + { + additionalProperties.Add(resolvedType); + } + } - // we've not been able to find a declared object type for the containing object, or it doesn't contain a property matching this one. - // best we can do is to simply generate a property for the assigned type. - return new TypeProperty(group.Key, resolvedType, TypePropertyFlags.Required); - }); + if (child is SpreadExpressionSyntax spreadSyntax) + { + var type = typeManager.GetTypeInfo(spreadSyntax.Expression); + if (type is ObjectType spreadType) + { + foreach (var (name, property) in spreadType.Properties) + { + namedProperties[name] = property; + } - var additionalProperties = syntax.Properties - .Where(p => p.TryGetKeyText() is null) - .Select(p => typeManager.GetTypeInfo(p)); + if (spreadType.AdditionalPropertiesType is {}) + { + additionalProperties.Add(spreadType.AdditionalPropertiesType.Type); + } + } + else + { + additionalProperties.Add(LanguageConstants.Any); + } + } + } var additionalPropertiesType = additionalProperties.Any() ? TypeHelper.CreateTypeUnion(additionalProperties) : null; // TODO: Add structural naming? - return new ObjectType(LanguageConstants.Object.Name, TypeSymbolValidationFlags.Default, namedProperties, additionalPropertiesType); + return new ObjectType(LanguageConstants.Object.Name, TypeSymbolValidationFlags.Default, namedProperties.Values, additionalPropertiesType); }); public override void VisitObjectPropertySyntax(ObjectPropertySyntax syntax) @@ -1271,22 +1317,70 @@ public override void VisitArraySyntax(ArraySyntax syntax) { var errors = new List(); - var itemTypes = new List(syntax.Children.Length); - TupleTypeNameBuilder typeName = new(); - foreach (var arrayItem in syntax.Items) + var childTypes = new List(syntax.Children.Length); + + foreach (var child in syntax.Children) { - var itemType = typeManager.GetTypeInfo(arrayItem); - itemTypes.Add(itemType); - typeName.AppendItem(itemType.Name); - CollectErrors(errors, itemType); + var childType = child switch { + ArrayItemSyntax x => typeManager.GetTypeInfo(x.Value), + SpreadExpressionSyntax x => typeManager.GetTypeInfo(x.Expression), + _ => null, + }; + + if (childType is null) + { + continue; + } + + if (child is SpreadExpressionSyntax spread && + childType is not ErrorType && + !TypeValidator.AreTypesAssignable(childType, LanguageConstants.Array)) + { + childType = ErrorType.Create(DiagnosticBuilder.ForPosition(child).SpreadOperatorRequiresAssignableValue(spread, LanguageConstants.Array)); + } + + CollectErrors(errors, childType); + childTypes.Add(childType); } - if (PropagateErrorType(errors, itemTypes)) + if (PropagateErrorType(errors, childTypes)) { return ErrorType.Create(errors); } - return new TupleType(typeName.ToString(), itemTypes.ToImmutableArray(), TypeSymbolValidationFlags.Default); + var tupleEntries = new List(); + var itemEntries = new List(); + foreach (var child in syntax.Children) + { + if (child is ArrayItemSyntax arrayItemSyntax) + { + tupleEntries.Add(typeManager.GetTypeInfo(child)); + } + else if (child is SpreadExpressionSyntax spreadSyntax) + { + var type = typeManager.GetTypeInfo(spreadSyntax.Expression); + if (type is TupleType spreadType) + { + tupleEntries.AddRange(spreadType.Items.Select(x => x.Type)); + } + else if (type is ArrayType arrayType) + { + itemEntries.Add(arrayType.Item.Type); + } + else + { + itemEntries.Add(LanguageConstants.Any); + } + } + } + + if (itemEntries.Any()) + { + var itemType = TypeHelper.CreateTypeUnion(tupleEntries.Concat(itemEntries)); + return new TypedArrayType(itemType, TypeSymbolValidationFlags.Default); + } + + return new TupleType(tupleEntries.ToImmutableArray(), TypeSymbolValidationFlags.Default); }); public override void VisitTernaryOperationSyntax(TernaryOperationSyntax syntax) diff --git a/src/Bicep.Decompiler.IntegrationTests/Files/Working/lambdas/main.bicep b/src/Bicep.Decompiler.IntegrationTests/Files/Working/lambdas/main.bicep index 430620ab1f9..2227199fdd1 100644 --- a/src/Bicep.Decompiler.IntegrationTests/Files/Working/lambdas/main.bicep +++ b/src/Bicep.Decompiler.IntegrationTests/Files/Working/lambdas/main.bicep @@ -6,16 +6,16 @@ var doggos = [ ] var numbers = range(0, 4) var sayHello = map(doggos, i => 'Hello ${i}!') -//@[4:12) [no-unused-vars (Warning)] Variable "sayHello" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sayHello| +//@[04:12) [no-unused-vars (Warning)] Variable "sayHello" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sayHello| var isEven = filter(numbers, i => (0 == (i % 2))) -//@[4:10) [no-unused-vars (Warning)] Variable "isEven" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |isEven| +//@[04:10) [no-unused-vars (Warning)] Variable "isEven" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |isEven| var evenDoggosNestedLambdas = map( -//@[4:27) [no-unused-vars (Warning)] Variable "evenDoggosNestedLambdas" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenDoggosNestedLambdas| +//@[04:27) [no-unused-vars (Warning)] Variable "evenDoggosNestedLambdas" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |evenDoggosNestedLambdas| filter(numbers, i => contains(filter(numbers, j => (0 == (j % 2))), i)), x => doggos[x] ) var flattenedArrayOfArrays = flatten([ -//@[4:26) [no-unused-vars (Warning)] Variable "flattenedArrayOfArrays" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedArrayOfArrays| +//@[04:26) [no-unused-vars (Warning)] Variable "flattenedArrayOfArrays" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedArrayOfArrays| [ 0 1 @@ -30,9 +30,9 @@ var flattenedArrayOfArrays = flatten([ ] ]) var flattenedEmptyArray = flatten([]) -//@[4:23) [no-unused-vars (Warning)] Variable "flattenedEmptyArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedEmptyArray| +//@[04:23) [no-unused-vars (Warning)] Variable "flattenedEmptyArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |flattenedEmptyArray| var mapSayHi = map( -//@[4:12) [no-unused-vars (Warning)] Variable "mapSayHi" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapSayHi| +//@[04:12) [no-unused-vars (Warning)] Variable "mapSayHi" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapSayHi| [ 'abc' 'def' @@ -41,7 +41,7 @@ var mapSayHi = map( foo => 'Hi ${foo}!' ) var mapEmpty = map([], foo => 'Hi ${foo}!') -//@[4:12) [no-unused-vars (Warning)] Variable "mapEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapEmpty| +//@[04:12) [no-unused-vars (Warning)] Variable "mapEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapEmpty| var mapObject = map( range(0, length(doggos)), i => { @@ -51,7 +51,7 @@ var mapObject = map( } ) var mapArray = flatten(map( -//@[4:12) [no-unused-vars (Warning)] Variable "mapArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapArray| +//@[04:12) [no-unused-vars (Warning)] Variable "mapArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapArray| range(1, 3), i => [ (i * 2) @@ -59,7 +59,7 @@ var mapArray = flatten(map( ] )) var mapMultiLineArray = flatten(map( -//@[4:21) [no-unused-vars (Warning)] Variable "mapMultiLineArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapMultiLineArray| +//@[04:21) [no-unused-vars (Warning)] Variable "mapMultiLineArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |mapMultiLineArray| range(1, 3), i => [ (i * 3) @@ -68,7 +68,7 @@ var mapMultiLineArray = flatten(map( ] )) var filterEqualityCheck = filter( -//@[4:23) [no-unused-vars (Warning)] Variable "filterEqualityCheck" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEqualityCheck| +//@[04:23) [no-unused-vars (Warning)] Variable "filterEqualityCheck" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEqualityCheck| [ 'abc' 'def' @@ -77,9 +77,9 @@ var filterEqualityCheck = filter( foo => ('def' == foo) ) var filterEmpty = filter([], foo => ('def' == foo)) -//@[4:15) [no-unused-vars (Warning)] Variable "filterEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEmpty| +//@[04:15) [no-unused-vars (Warning)] Variable "filterEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filterEmpty| var sortNumeric = sort( -//@[4:15) [no-unused-vars (Warning)] Variable "sortNumeric" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortNumeric| +//@[04:15) [no-unused-vars (Warning)] Variable "sortNumeric" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortNumeric| [ 8 3 @@ -90,7 +90,7 @@ var sortNumeric = sort( (x, y) => (x < y) ) var sortAlpha = sort( -//@[4:13) [no-unused-vars (Warning)] Variable "sortAlpha" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlpha| +//@[04:13) [no-unused-vars (Warning)] Variable "sortAlpha" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlpha| [ 'ghi' 'abc' @@ -99,7 +99,7 @@ var sortAlpha = sort( (x, y) => (x < y) ) var sortAlphaReverse = sort( -//@[4:20) [no-unused-vars (Warning)] Variable "sortAlphaReverse" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlphaReverse| +//@[04:20) [no-unused-vars (Warning)] Variable "sortAlphaReverse" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortAlphaReverse| [ 'ghi' 'abc' @@ -129,9 +129,9 @@ var sortByObjectKey = sort( (x, y) => (int(x.key) < int(y.key)) ) var sortEmpty = sort([], (x, y) => (int(x) < int(y))) -//@[4:13) [no-unused-vars (Warning)] Variable "sortEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortEmpty| +//@[04:13) [no-unused-vars (Warning)] Variable "sortEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |sortEmpty| var reduceStringConcat = reduce( -//@[4:22) [no-unused-vars (Warning)] Variable "reduceStringConcat" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceStringConcat| +//@[04:22) [no-unused-vars (Warning)] Variable "reduceStringConcat" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceStringConcat| [ 'abc' 'def' @@ -139,11 +139,12 @@ var reduceStringConcat = reduce( ], '', (cur, next) => concat(cur, next) +//@[17:34) [prefer-interpolation (Warning)] Use string interpolation instead of the concat function. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-interpolation)) |concat(cur, next)| ) var reduceFactorial = reduce(range(1, 5), 1, (cur, next) => (cur * next)) -//@[4:19) [no-unused-vars (Warning)] Variable "reduceFactorial" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceFactorial| +//@[04:19) [no-unused-vars (Warning)] Variable "reduceFactorial" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceFactorial| var reduceObjectUnion = reduce( -//@[4:21) [no-unused-vars (Warning)] Variable "reduceObjectUnion" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceObjectUnion| +//@[04:21) [no-unused-vars (Warning)] Variable "reduceObjectUnion" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceObjectUnion| [ { foo: 123 @@ -159,37 +160,38 @@ var reduceObjectUnion = reduce( (cur, next) => union(cur, next) ) var reduceEmpty = reduce([], 0, (cur, next) => cur) -//@[4:15) [no-unused-vars (Warning)] Variable "reduceEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceEmpty| +//@[04:15) [no-unused-vars (Warning)] Variable "reduceEmpty" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |reduceEmpty| var filteredLoop = filter(itemForLoop, i => (i > 5)) -//@[4:16) [no-unused-vars (Warning)] Variable "filteredLoop" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filteredLoop| +//@[04:16) [no-unused-vars (Warning)] Variable "filteredLoop" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |filteredLoop| var parentheses = map( -//@[4:15) [no-unused-vars (Warning)] Variable "parentheses" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |parentheses| +//@[04:15) [no-unused-vars (Warning)] Variable "parentheses" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |parentheses| [ 123 ], i => i ) var objectMap = toObject( -//@[4:13) [no-unused-vars (Warning)] Variable "objectMap" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap| +//@[04:13) [no-unused-vars (Warning)] Variable "objectMap" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap| [ 123 456 789 ], i => (i / 100) -//@[2:16) [BCP070 (Error)] Argument of type "any => int" is not assignable to parameter of type "any => string". (CodeDescription: none) |i => (i / 100)| +//@[02:16) [BCP070 (Error)] Argument of type "(123 | 456 | 789) => int" is not assignable to parameter of type "any => string". (CodeDescription: none) |i => (i / 100)| ) var objectMap2 = toObject( -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap2| +//@[04:14) [no-unused-vars (Warning)] Variable "objectMap2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap2| range(0, 10), i => i, +//@[02:08) [BCP070 (Error)] Argument of type "int => int" is not assignable to parameter of type "any => string". (CodeDescription: none) |i => i| i => { isEven: ((i % 2) == 0) isGreaterThan4: (i > 4) } ) var objectMap3 = toObject(sortByObjectKey, x => x.name) -//@[4:14) [no-unused-vars (Warning)] Variable "objectMap3" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap3| +//@[04:14) [no-unused-vars (Warning)] Variable "objectMap3" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |objectMap3| var itemForLoop = [for i in range(0, length(range(0, 10))): range(0, 10)[i]] module asdfsadf './nested_asdfsadf.bicep' = {