Skip to content

Commit a216e83

Browse files
authored
fix and add test for invalid parameter type (#17193)
Fixes #16731 Adding diagnostic error when there is a type mismatch between the parameter in the bicep file and the parameter in the bicepparam file. Also adding a test to verify the error message is correct ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/17193)
1 parent 314f88e commit a216e83

File tree

2 files changed

+80
-5
lines changed

2 files changed

+80
-5
lines changed

src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,5 +645,67 @@ param intParam int
645645
File.Exists(Path.Combine(bicepOutputPath, outputFile)).Should().Be(true, f);
646646
}
647647
}
648+
649+
[TestMethod]
650+
public async Task BuildParams_Extends_InvalidType_ThrowsError()
651+
{
652+
var outputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
653+
FileHelper.SaveResultFile(TestContext, "main.bicep", @"
654+
param tag string
655+
", outputPath);
656+
FileHelper.SaveResultFile(TestContext, "base.bicepparam", @"
657+
using none
658+
param tag = 42
659+
", outputPath);
660+
var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicepparam", @"
661+
using 'main.bicep'
662+
extends 'base.bicepparam'
663+
", outputPath);
664+
665+
var expectedOutputFile = FileHelper.GetResultFilePath(TestContext, "main.json", outputPath);
666+
File.Exists(expectedOutputFile).Should().BeFalse();
667+
668+
var (output, error, result) = await Bicep(["build-params", inputFile]);
669+
670+
File.Exists(expectedOutputFile).Should().BeFalse();
671+
672+
output.Should().BeEmpty();
673+
error.Should().Contain("Error BCP033: Expected a value of type \"string\" but the provided value is of type \"42\".");
674+
result.Should().Be(1);
675+
}
676+
677+
[TestMethod]
678+
public async Task BuildParams_Extends_Multiple_InvalidType_ThrowsMultipleErrors()
679+
{
680+
var outputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
681+
FileHelper.SaveResultFile(TestContext, "main.bicep", @"
682+
param myString string
683+
param myInt int
684+
param myBool bool
685+
", outputPath);
686+
FileHelper.SaveResultFile(TestContext, "base.bicepparam", @"
687+
using none
688+
param myInt = '42'
689+
param myString = {}
690+
param myBool = []
691+
", outputPath);
692+
var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicepparam", @"
693+
using './main.bicep'
694+
extends 'base.bicepparam'
695+
", outputPath);
696+
697+
var expectedOutputFile = FileHelper.GetResultFilePath(TestContext, "main.json", outputPath);
698+
File.Exists(expectedOutputFile).Should().BeFalse();
699+
700+
var (output, error, result) = await Bicep(["build-params", inputFile]);
701+
702+
File.Exists(expectedOutputFile).Should().BeFalse();
703+
704+
output.Should().BeEmpty();
705+
error.Should().Contain("Error BCP033: Expected a value of type \"int\" but the provided value is of type \"'42'\".");
706+
error.Should().Contain("Error BCP033: Expected a value of type \"string\" but the provided value is of type \"object\".");
707+
error.Should().Contain("Error BCP033: Expected a value of type \"bool\" but the provided value is of type \"<empty array>\".");
708+
result.Should().Be(1);
709+
}
648710
}
649711
}

src/Bicep.Core/Semantics/SemanticModel.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -700,17 +700,30 @@ private IEnumerable<IDiagnostic> GatherMissingRequiredExtensionConfigAssignmentD
700700

701701
private IEnumerable<IDiagnostic> GatherTypeMismatchDiagnostics()
702702
{
703-
foreach (var assignmentSymbol in Root.ParameterAssignments.Where(x => x.Context.SourceFile == Root.Context.SourceFile))
703+
foreach (var assignmentSymbol in Root.ParameterAssignments)
704704
{
705+
var isFromSameFile = assignmentSymbol.Context.SourceFile == Root.Context.SourceFile;
706+
705707
if (assignmentSymbol.Type is not ErrorType &&
706708
assignmentSymbol.Type is not NullType && // `param x = null` is equivalent to skipping the assignment altogether
707709
TypeManager.GetDeclaredType(assignmentSymbol.DeclaringSyntax) is { } declaredType)
708710
{
709-
var diagnostics = ToListDiagnosticWriter.Create();
710-
TypeValidator.NarrowTypeAndCollectDiagnostics(TypeManager, Binder, ParsingErrorLookup, diagnostics, assignmentSymbol.DeclaringParameterAssignment.Value, declaredType);
711-
foreach (var diagnostic in diagnostics.GetDiagnostics())
711+
if (isFromSameFile)
712+
{
713+
var diagnostics = ToListDiagnosticWriter.Create();
714+
TypeValidator.NarrowTypeAndCollectDiagnostics(TypeManager, Binder, ParsingErrorLookup, diagnostics, assignmentSymbol.DeclaringParameterAssignment.Value, declaredType);
715+
foreach (var diagnostic in diagnostics.GetDiagnostics())
716+
{
717+
yield return diagnostic;
718+
}
719+
}
720+
else
712721
{
713-
yield return diagnostic;
722+
var areTypesAssignable = TypeValidator.AreTypesAssignable(assignmentSymbol.Type, declaredType);
723+
if (!areTypesAssignable)
724+
{
725+
yield return DiagnosticBuilder.ForPosition(assignmentSymbol.DeclaringSyntax).ExpectedValueTypeMismatch(false, declaredType, assignmentSymbol.Type);
726+
}
714727
}
715728
}
716729
}

0 commit comments

Comments
 (0)