Skip to content

Commit

Permalink
QuickFix support for property name and string literals (Azure#655)
Browse files Browse the repository at this point in the history
* QuickFix support for property name and string literals

* Spelling check for discriminator key

* Added a test sample for property name spelling suggestion
  • Loading branch information
shenglol authored Oct 15, 2020
1 parent 04d9e60 commit bf88474
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 45 deletions.
66 changes: 63 additions & 3 deletions src/Bicep.Core.IntegrationTests/TypeSystem/TypeValidationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,48 @@ public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolVa
);
}

{
// incorrect discriminator key case
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
DiscKey: 'choiceA'
}
}
}
";

var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP078", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" requires a value of type \"'choiceA' | 'choiceB'\", but none was supplied."),
x => x.Should().HaveCodeAndSeverity("BCP089", expectedDiagnosticLevel).And.HaveMessage("The property \"DiscKey\" is not allowed on objects of type \"'choiceA' | 'choiceB'\". Did you mean \"discKey\"?")
);
}

{
// incorrect discriminator key
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'foo'
}
}
}
";

var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP036", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'foo'\".")
);
}

{
// incorrect discriminator key with suggestion
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
Expand All @@ -237,7 +276,7 @@ public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolVa

var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP036", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'choiceC'\".")
x => x.Should().HaveCodeAndSeverity("BCP088", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'choiceC'\". Did you mean \"'choiceA'\"?")
);
}

Expand All @@ -261,7 +300,28 @@ public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolVa
}

{
// all good
// discriminator key supplied, case of required property is incorrect
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'choiceA'
ValueA: 'hello'
}
}
}
";

var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP035", expectedDiagnosticLevel).And.HaveMessage("The specified object is missing the following required properties: \"valueA\"."),
x => x.Should().HaveCodeAndSeverity("BCP089", expectedDiagnosticLevel).And.HaveMessage("The property \"ValueA\" is not allowed on objects of type \"choiceA\". Did you mean \"valueA\"?")
);
}

{
// all good, incorrect property access
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
Expand All @@ -284,7 +344,7 @@ public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolVa
}

{
// all good
// all good, incorrect property access with suggestion
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
Expand Down
9 changes: 8 additions & 1 deletion src/Bicep.Core.Samples/InvalidResources_CRLF/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,11 @@ resource discriminatorKeySetTwo 'Microsoft.Resources/deploymentScripts@2020-10-0
// #completionTest(0,1,2,3,4) -> deploymentScriptPSProperties

}
}
}

resource incorrectPropertiesKey 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
kind: 'AzureCLI'

propertes: {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,13 @@ resource discriminatorKeySetTwo 'Microsoft.Resources/deploymentScripts@2020-10-0

}
}

resource incorrectPropertiesKey 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
//@[85:132) [BCP035 (Error)] The specified object is missing the following required properties: "name". |{\r\n kind: 'AzureCLI'\r\n\r\n propertes: {\r\n }\r\n}|
kind: 'AzureCLI'

propertes: {
//@[2:11) [BCP089 (Error)] The property "propertes" is not allowed on objects of type "Microsoft.Resources/deploymentScripts@2020-10-01". Did you mean "properties"? |propertes|
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,12 @@ resource discriminatorKeySetTwo 'Microsoft.Resources/deploymentScripts@2020-10-0

}
}

resource incorrectPropertiesKey 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
//@[9:31) Resource incorrectPropertiesKey. Type: Microsoft.Resources/deploymentScripts@2020-10-01. Declaration start char: 0, length: 132
kind: 'AzureCLI'

propertes: {
}
}

39 changes: 38 additions & 1 deletion src/Bicep.Core.Samples/InvalidResources_CRLF/main.syntax.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -1041,4 +1041,41 @@ resource discriminatorKeySetTwo 'Microsoft.Resources/deploymentScripts@2020-10-0
//@[3:5) NewLine |\r\n|
}
//@[0:1) RightBrace |}|
//@[1:1) EndOfFile ||
//@[1:5) NewLine |\r\n\r\n|

resource incorrectPropertiesKey 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
//@[0:132) ResourceDeclarationSyntax
//@[0:8) Identifier |resource|
//@[9:31) IdentifierSyntax
//@[9:31) Identifier |incorrectPropertiesKey|
//@[32:82) StringSyntax
//@[32:82) StringComplete |'Microsoft.Resources/deploymentScripts@2020-10-01'|
//@[83:84) Assignment |=|
//@[85:132) ObjectSyntax
//@[85:86) LeftBrace |{|
//@[86:88) NewLine |\r\n|
kind: 'AzureCLI'
//@[2:18) ObjectPropertySyntax
//@[2:6) IdentifierSyntax
//@[2:6) Identifier |kind|
//@[6:7) Colon |:|
//@[8:18) StringSyntax
//@[8:18) StringComplete |'AzureCLI'|
//@[18:22) NewLine |\r\n\r\n|

propertes: {
//@[2:19) ObjectPropertySyntax
//@[2:11) IdentifierSyntax
//@[2:11) Identifier |propertes|
//@[11:12) Colon |:|
//@[13:19) ObjectSyntax
//@[13:14) LeftBrace |{|
//@[14:16) NewLine |\r\n|
}
//@[2:3) RightBrace |}|
//@[3:5) NewLine |\r\n|
}
//@[0:1) RightBrace |}|
//@[1:3) NewLine |\r\n|

//@[0:0) EndOfFile ||
29 changes: 28 additions & 1 deletion src/Bicep.Core.Samples/InvalidResources_CRLF/main.tokens.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -708,4 +708,31 @@ resource discriminatorKeySetTwo 'Microsoft.Resources/deploymentScripts@2020-10-0
//@[3:5) NewLine |\r\n|
}
//@[0:1) RightBrace |}|
//@[1:1) EndOfFile ||
//@[1:5) NewLine |\r\n\r\n|

resource incorrectPropertiesKey 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
//@[0:8) Identifier |resource|
//@[9:31) Identifier |incorrectPropertiesKey|
//@[32:82) StringComplete |'Microsoft.Resources/deploymentScripts@2020-10-01'|
//@[83:84) Assignment |=|
//@[85:86) LeftBrace |{|
//@[86:88) NewLine |\r\n|
kind: 'AzureCLI'
//@[2:6) Identifier |kind|
//@[6:7) Colon |:|
//@[8:18) StringComplete |'AzureCLI'|
//@[18:22) NewLine |\r\n\r\n|

propertes: {
//@[2:11) Identifier |propertes|
//@[11:12) Colon |:|
//@[13:14) LeftBrace |{|
//@[14:16) NewLine |\r\n|
}
//@[2:3) RightBrace |}|
//@[3:5) NewLine |\r\n|
}
//@[0:1) RightBrace |}|
//@[1:3) NewLine |\r\n|

//@[0:0) EndOfFile ||
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ public void DiscriminatedObjectType_raises_appropriate_diagnostics_for_matches()

diagnostics.Should().SatisfyRespectively(
x => {
x.Message.Should().Be("The property \"myDiscriminator\" expected a value of type \"'valA' | 'valB'\" but the provided value is of type \"'valC'\".");
x.Message.Should().Be("The property \"myDiscriminator\" expected a value of type \"'valA' | 'valB'\" but the provided value is of type \"'valC'\". Did you mean \"'valA'\"?");
});
narrowedType.Should().BeOfType<AnyType>();
}
Expand Down
15 changes: 15 additions & 0 deletions src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,21 @@ public ErrorDiagnostic ArgumentCountMismatch(int argumentCount, int mininumArgum
TextSpan,
"BCP087",
"Array and object literals are not allowed here.");

public FixableDiagnostic PropertyStringLiteralMismatchWithSuggestion(bool warnInsteadOfError, string property, TypeSymbol expectedType, string actualStringLiteral, string suggestedStringLiteral) => new FixableDiagnostic(
TextSpan,
warnInsteadOfError ? DiagnosticLevel.Warning : DiagnosticLevel.Error,
"BCP088",
$"The property \"{property}\" expected a value of type \"{expectedType}\" but the provided value is of type \"{actualStringLiteral}\". Did you mean \"{suggestedStringLiteral}\"?",
new CodeFix($"Change \"{actualStringLiteral}\" to \"{suggestedStringLiteral}\"", true, CodeManipulator.Replace(TextSpan, suggestedStringLiteral)));

public FixableDiagnostic DisallowedPropertyWithSuggestion(bool warnInsteadOfError, string property, TypeSymbol type, string suggestedProperty) => new FixableDiagnostic(
TextSpan,
warnInsteadOfError ? DiagnosticLevel.Warning : DiagnosticLevel.Error,
"BCP089",
$"The property \"{property}\" is not allowed on objects of type \"{type}\". Did you mean \"{suggestedProperty}\"?",
new CodeFix($"Change \"{property}\" to \"{suggestedProperty}\"", true, CodeManipulator.Replace(TextSpan, suggestedProperty)));

}

public static DiagnosticBuilderInternal ForPosition(TextSpan span)
Expand Down
Loading

0 comments on commit bf88474

Please sign in to comment.