diff --git a/src/Bicep.Core.IntegrationTests/ScenarioTests.cs b/src/Bicep.Core.IntegrationTests/ScenarioTests.cs index 830acf0d870..11c60b203bf 100644 --- a/src/Bicep.Core.IntegrationTests/ScenarioTests.cs +++ b/src/Bicep.Core.IntegrationTests/ScenarioTests.cs @@ -4955,6 +4955,19 @@ public void Test_Issue502() ("BCP353", DiagnosticLevel.Error, "The types \"foo\", \"FoO\" differ only in casing. The ARM deployments engine is not case sensitive and will not be able to distinguish between them."), }); + result = CompilationHelper.Compile(Services.WithFeatureOverrides(new(UserDefinedTypesEnabled: true)), """ + param x { + foo: string + FoO: int + } + """); + + result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new[] + { + ("BCP353", DiagnosticLevel.Error, "The type properties \"foo\", \"FoO\" differ only in casing. The ARM deployments engine is not case sensitive and will not be able to distinguish between them."), + ("BCP353", DiagnosticLevel.Error, "The type properties \"foo\", \"FoO\" differ only in casing. The ARM deployments engine is not case sensitive and will not be able to distinguish between them."), + }); + result = CompilationHelper.Compile(Services.WithFeatureOverrides(new(AssertsEnabled: true)), """ assert foo = true assert FoO = true diff --git a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs index 18a46f7ee4d..bd0e812c535 100644 --- a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs +++ b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs @@ -1981,10 +1981,10 @@ public ErrorDiagnostic RuntimeValueNotAllowedInFunctionDeclaration(string? acces "BCP352", $"Failed to evaluate variable \"{name}\": {message}"); - public ErrorDiagnostic SymbolsMustBeCaseInsensitivelyUnique(string symbolTypePluralName, IEnumerable symbolNames) => new( + public ErrorDiagnostic ItemsMustBeCaseInsensitivelyUnique(string itemTypePluralName, IEnumerable itemNames) => new( TextSpan, "BCP353", - $"The {symbolTypePluralName} {ToQuotedString(symbolNames)} differ only in casing. The ARM deployments engine is not case sensitive and will not be able to distinguish between them."); + $"The {itemTypePluralName} {ToQuotedString(itemNames)} differ only in casing. The ARM deployments engine is not case sensitive and will not be able to distinguish between them."); } public static DiagnosticBuilderInternal ForPosition(TextSpan span) diff --git a/src/Bicep.Core/Emit/EmitLimitationCalculator.cs b/src/Bicep.Core/Emit/EmitLimitationCalculator.cs index a9eb50fd5eb..4a73d325764 100644 --- a/src/Bicep.Core/Emit/EmitLimitationCalculator.cs +++ b/src/Bicep.Core/Emit/EmitLimitationCalculator.cs @@ -7,14 +7,15 @@ using System.Linq; using Bicep.Core.DataFlow; using Bicep.Core.Diagnostics; +using Bicep.Core.Extensions; +using Bicep.Core.Parsing; using Bicep.Core.Semantics; using Bicep.Core.Semantics.Metadata; using Bicep.Core.Syntax; +using Bicep.Core.Syntax.Visitors; using Bicep.Core.TypeSystem; using Bicep.Core.TypeSystem.Az; using Bicep.Core.Utils; -using Bicep.Core.Extensions; -using Bicep.Core.Syntax.Visitors; using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; using Newtonsoft.Json.Linq; @@ -603,24 +604,33 @@ private static void BlockNamesDistinguishedOnlyByCase(SemanticModel model, IDiag ("asserts", model.Root.AssertDeclarations), }) { - BlockCaseInsensitiveNameClashes(symbolTypePluralName, symbolsOfType, diagnostics); + BlockCaseInsensitiveNameClashes(symbolTypePluralName, symbolsOfType, s => s.Name, s => s.NameSource, diagnostics); + } + + foreach (var objectTypeDeclaration in SyntaxAggregator.AggregateByType(model.SourceFile.ProgramSyntax)) + { + BlockCaseInsensitiveNameClashes("type properties", + objectTypeDeclaration.Properties.SelectMany(p => p.TryGetKeyText() is string key ? (key, p.Key).AsEnumerable() : Enumerable.Empty<(string, SyntaxBase)>()), + t => t.Item1, + t => t.Item2, + diagnostics); } } - private static void BlockCaseInsensitiveNameClashes(string symbolTypePluralName, IEnumerable symbolsOfType, IDiagnosticWriter diagnostics) + private static void BlockCaseInsensitiveNameClashes(string itemTypePluralName, IEnumerable itemsOfType, Func nameExtractor, Func nameSyntaxExtractor, IDiagnosticWriter diagnostics) { - foreach (var grouping in symbolsOfType.ToLookup(s => s.Name, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1)) + foreach (var grouping in itemsOfType.ToLookup(nameExtractor, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1)) { - var clashingSymbols = grouping.Select(s => s.Name).ToArray(); + var clashingNames = grouping.Select(nameExtractor).ToArray(); // if any symbols are exact matches, a different diagnostic about multiple declarations will have already been raised - if (clashingSymbols.Distinct().Count() != clashingSymbols.Length) + if (clashingNames.Distinct().Count() != clashingNames.Length) { continue; } diagnostics.WriteMultiple(grouping.Select( - symbol => DiagnosticBuilder.ForPosition(symbol.NameSource).SymbolsMustBeCaseInsensitivelyUnique(symbolTypePluralName, clashingSymbols))); + item => DiagnosticBuilder.ForPosition(nameSyntaxExtractor(item)).ItemsMustBeCaseInsensitivelyUnique(itemTypePluralName, clashingNames))); } } }