Skip to content

Commit

Permalink
Add a linter rule to suggest using identifier tokens and property acc…
Browse files Browse the repository at this point in the history
…ess for object properties when the property name permits it. (Azure#6584)

* Add a linter rule to suggest using identifier tokens and property access for object properties when the property name permits it.

* Correct property names linter quick fix span selection

* Update config schema to include new linter rule

* Update integration tests to reflect desired property access pattern

* Update baselinable artifacts

* Run automated test baseline update script

* Revert unecessary manual test changes

* Run automated test baseline update script

* Oops, missed a manual test reversion

* Run automated test baseline update script

* Update code fix titles

* Disable linter in extensibility test

* Rename rule

* Run automated test baseline update script

* Update rule name in non-baseline test artifacts

* Update rule description

* Update rule description

* Update quickfix text for property access prompt

* Run automated test baseline update script

* Incorporate PR feedback

* Set baseline

* Fix tests
  • Loading branch information
jeskew authored Jun 28, 2022
1 parent ae66808 commit 13f06cf
Show file tree
Hide file tree
Showing 17 changed files with 456 additions and 89 deletions.
4 changes: 4 additions & 0 deletions src/Bicep.Core.IntegrationTests/ExtensibilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ import storage as stg {
}]
output sourceContainerName string = container.name
#disable-next-line prefer-unquoted-property-names
output sourceContainerNameSquare string = container['name']
output miscBlobContainerName string = blobs[13 % 10].containerName
output containerName string = blobs[5].containerName
#disable-next-line prefer-unquoted-property-names
output base64Content string = blobs[3]['base64Content']
");
result.Should().NotHaveAnyDiagnostics();
Expand All @@ -134,8 +136,10 @@ param numApps int
}]
output myAppId string = myApp.appId
#disable-next-line prefer-unquoted-property-names
output myAppId2 string = myApp['appId']
output myAppsLoopId string = myAppsLoop[13 % numApps].appId
#disable-next-line prefer-unquoted-property-names
output myAppsLoopId2 string = myAppsLoop[3]['appId']
");
result.Should().NotHaveAnyDiagnostics();
Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core.Samples/Files/Completions/declarations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@
"detail": "DNS Record",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' = {\n name: 'name'\n location: location\n}\n\nresource dnsRecord 'Microsoft.Network/dnsZones/A@2018-05-01' = {\n parent: dnsZone\n name: 'name'\n properties: {\n TTL: 3600\n 'ARecords': []\n }\n}\n\n```"
"value": "```bicep\nresource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' = {\n name: 'name'\n location: location\n}\n\nresource dnsRecord 'Microsoft.Network/dnsZones/A@2018-05-01' = {\n parent: dnsZone\n name: 'name'\n properties: {\n TTL: 3600\n ARecords: []\n }\n}\n\n```"
},
"deprecated": false,
"preselect": false,
Expand All @@ -1136,7 +1136,7 @@
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' = {\n name: ${1:'name'}\n location: ${2:location}\n}\n\nresource ${3:dnsRecord} 'Microsoft.Network/dnsZones/${4|A,AAAA,CNAME,MX,NS,PTR,SOA,SRV,TXT|}@2018-05-01' = {\n parent: dnsZone\n name: ${5:'name'}\n properties: {\n TTL: 3600\n '${6|ARecords,AAAARecords,MXRecords,NSRecords,PTRRecords,SRVRecords,TXTRecords,CNAMERecord,SOARecord|}': []\n }\n}\n"
"newText": "resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' = {\n name: ${1:'name'}\n location: ${2:location}\n}\n\nresource ${3:dnsRecord} 'Microsoft.Network/dnsZones/${4|A,AAAA,CNAME,MX,NS,PTR,SOA,SRV,TXT|}@2018-05-01' = {\n parent: dnsZone\n name: ${5:'name'}\n properties: {\n TTL: 3600\n ${6|ARecords,AAAARecords,MXRecords,NSRecords,PTRRecords,SRVRecords,TXTRecords,CNAMERecord,SOARecord|}: []\n }\n}\n"
},
"command": {
"title": "top level snippet completion",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ var integerIndexOnNonArray = (null)[0]
var stringIndexOnNonObject = 'test'['test']
//@[04:26) [no-unused-vars (Warning)] Variable "stringIndexOnNonObject" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |stringIndexOnNonObject|
//@[29:35) [BCP076 (Error)] Cannot index over expression of type "'test'". Arrays or objects are required. (CodeDescription: none) |'test'|
//@[35:43) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['test']|
var malformedStringIndex = {
//@[04:24) [no-unused-vars (Warning)] Variable "malformedStringIndex" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |malformedStringIndex|
}['test\e']
Expand All @@ -251,6 +252,7 @@ var badIndexOverArray = [][null]
//@[27:31) [BCP074 (Error)] Indexing over arrays requires an index of type "int" but the provided index was of type "null". (CodeDescription: none) |null|
var badIndexOverArray2 = []['s']
//@[04:22) [no-unused-vars (Warning)] Variable "badIndexOverArray2" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badIndexOverArray2|
//@[27:32) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['s']|
//@[28:31) [BCP074 (Error)] Indexing over arrays requires an index of type "int" but the provided index was of type "'s'". (CodeDescription: none) |'s'|
var badIndexOverObj = {}[true]
//@[04:19) [no-unused-vars (Warning)] Variable "badIndexOverObj" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badIndexOverObj|
Expand Down Expand Up @@ -405,6 +407,7 @@ var badSpelling = sampleObject.myNul
//@[31:36) [BCP083 (Error)] The type "object" does not contain property "myNul". Did you mean "myNull"? (CodeDescription: none) |myNul|
var badPropertyIndexer = sampleObject['fake']
//@[04:22) [no-unused-vars (Warning)] Variable "badPropertyIndexer" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badPropertyIndexer|
//@[37:45) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['fake']|
//@[38:44) [BCP053 (Error)] The type "object" does not contain property "fake". Available properties include "myArr", "myBool", "myInner", "myInt", "myNull", "myStr". (CodeDescription: none) |'fake'|
var badType = sampleObject.myStr / 32
//@[04:11) [no-unused-vars (Warning)] Variable "badType" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badType|
Expand All @@ -417,16 +420,20 @@ var badInnerType = sampleObject.myInner.anotherStr + 2
//@[19:54) [BCP045 (Error)] Cannot apply operator "+" to operands of type "'a'" and "int". (CodeDescription: none) |sampleObject.myInner.anotherStr + 2|
var badArrayIndexer = sampleObject.myArr['s']
//@[04:19) [no-unused-vars (Warning)] Variable "badArrayIndexer" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badArrayIndexer|
//@[40:45) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['s']|
//@[41:44) [BCP074 (Error)] Indexing over arrays requires an index of type "int" but the provided index was of type "'s'". (CodeDescription: none) |'s'|
var badInnerArrayIndexer = sampleObject.myInner.otherArr['s']
//@[04:24) [no-unused-vars (Warning)] Variable "badInnerArrayIndexer" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badInnerArrayIndexer|
//@[56:61) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['s']|
//@[57:60) [BCP074 (Error)] Indexing over arrays requires an index of type "int" but the provided index was of type "'s'". (CodeDescription: none) |'s'|
var badIndexer = sampleObject.myStr['s']
//@[04:14) [no-unused-vars (Warning)] Variable "badIndexer" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badIndexer|
//@[17:35) [BCP076 (Error)] Cannot index over expression of type "'s'". Arrays or objects are required. (CodeDescription: none) |sampleObject.myStr|
//@[35:40) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['s']|
var badInnerArray = sampleObject.myInner.fakeArr['s']
//@[04:17) [no-unused-vars (Warning)] Variable "badInnerArray" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |badInnerArray|
//@[41:48) [BCP053 (Error)] The type "object" does not contain property "fakeArr". Available properties include "anotherStr", "otherArr". (CodeDescription: none) |fakeArr|
//@[48:53) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['s']|
var invalidPropertyCallOnInstanceFunctionAccess = a.b.c.bar().baz
//@[04:47) [no-unused-vars (Warning)] Variable "invalidPropertyCallOnInstanceFunctionAccess" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |invalidPropertyCallOnInstanceFunctionAccess|
//@[50:51) [BCP057 (Error)] The name "a" does not exist in the current context. (CodeDescription: none) |a|
Expand Down Expand Up @@ -543,17 +550,21 @@ var partialObject = {
//@[02:03) [BCP022 (Error)] Expected a property name at this location. (CodeDescription: none) |3|

's'
//@[02:05) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |'s'|
//@[02:05) [BCP025 (Error)] The property "s" is declared multiple times in this object. Remove or rename the duplicate properties. (CodeDescription: none) |'s'|
//@[06:06) [BCP018 (Error)] Expected the ":" character at this location. (CodeDescription: none) ||
's' \
//@[02:05) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |'s'|
//@[02:05) [BCP025 (Error)] The property "s" is declared multiple times in this object. Remove or rename the duplicate properties. (CodeDescription: none) |'s'|
//@[06:07) [BCP018 (Error)] Expected the ":" character at this location. (CodeDescription: none) |\|
//@[06:07) [BCP001 (Error)] The following token is not recognized: "\". (CodeDescription: none) |\|
//@[07:07) [BCP009 (Error)] Expected a literal value, an array, an object, a parenthesized expression, or a function call at this location. (CodeDescription: none) ||
'e' =
//@[02:05) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |'e'|
//@[08:09) [BCP018 (Error)] Expected the ":" character at this location. (CodeDescription: none) |=|
//@[09:09) [BCP009 (Error)] Expected a literal value, an array, an object, a parenthesized expression, or a function call at this location. (CodeDescription: none) ||
's' :
//@[02:05) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |'s'|
//@[02:05) [BCP025 (Error)] The property "s" is declared multiple times in this object. Remove or rename the duplicate properties. (CodeDescription: none) |'s'|
//@[07:07) [BCP009 (Error)] Expected a literal value, an array, an object, a parenthesized expression, or a function call at this location. (CodeDescription: none) ||

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ module modWithListKeysInCondition './main.bicep' = if (listKeys('foo', '2020-05-

module modANoName './modulea.bicep' = if ({ 'a': b }.a == true) {
//@[007:017) [BCP028 (Error)] Identifier "modANoName" is declared multiple times. Remove or rename the duplicates. (CodeDescription: none) |modANoName|
//@[044:047) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |'a'|

}

Expand Down Expand Up @@ -293,6 +294,7 @@ module runtimeInvalidModule1 'empty.bicep' = {
module runtimeInvalidModule2 'empty.bicep' = {
name: runtimeValidRes1['location']
//@[008:036) [BCP120 (Error)] This expression is being used in an assignment to the "name" property of the "module" type, which requires a value that can be calculated at the start of the deployment. Properties of runtimeValidRes1 which can be calculated at the start include "apiVersion", "id", "name", "type". (CodeDescription: none) |runtimeValidRes1['location']|
//@[024:036) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['location']|
}

module runtimeInvalidModule3 'empty.bicep' = {
Expand All @@ -303,16 +305,20 @@ module runtimeInvalidModule3 'empty.bicep' = {
module runtimeInvalidModule4 'empty.bicep' = {
name: runtimeValidRes1.sku['name']
//@[008:028) [BCP120 (Error)] This expression is being used in an assignment to the "name" property of the "module" type, which requires a value that can be calculated at the start of the deployment. Properties of runtimeValidRes1 which can be calculated at the start include "apiVersion", "id", "name", "type". (CodeDescription: none) |runtimeValidRes1.sku|
//@[028:036) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['name']|
}

module runtimeInvalidModule5 'empty.bicep' = {
name: runtimeValidRes1['sku']['name']
//@[008:031) [BCP120 (Error)] This expression is being used in an assignment to the "name" property of the "module" type, which requires a value that can be calculated at the start of the deployment. Properties of runtimeValidRes1 which can be calculated at the start include "apiVersion", "id", "name", "type". (CodeDescription: none) |runtimeValidRes1['sku']|
//@[024:031) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['sku']|
//@[031:039) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['name']|
}

module runtimeInvalidModule6 'empty.bicep' = {
name: runtimeValidRes1['sku'].name
//@[008:031) [BCP120 (Error)] This expression is being used in an assignment to the "name" property of the "module" type, which requires a value that can be calculated at the start of the deployment. Properties of runtimeValidRes1 which can be calculated at the start include "apiVersion", "id", "name", "type". (CodeDescription: none) |runtimeValidRes1['sku']|
//@[024:031) [prefer-unquoted-property-names (Warning)] Property names that are valid identifiers should be declared without quotation marks and accessed using dot notation. (CodeDescription: bicep core(https://aka.ms/bicep/linter/prefer-unquoted-property-names)) |['sku']|
}

module singleModuleForRuntimeCheck 'modulea.bicep' = {
Expand Down
Loading

0 comments on commit 13f06cf

Please sign in to comment.