From 338f769d31a0ae3bcccfeb1516c50ea688399562 Mon Sep 17 00:00:00 2001 From: Anthony Martin <38542602+anthony-c-martin@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:26:31 -0400 Subject: [PATCH] Convert `with` + `as` keywords into identifiers (#14360) Noticed when helping @polatengin on modular params that we have also implemented `with` and `as` as dedicated tokens, rather than identifiers. This means that the following cannot be parsed as legitimate Bicep code: ```bicep var with = 'with' ``` Also closes #13347 ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/14360) --- .../ProviderImportTests.cs | 2 +- .../baselines/Imports_LF/main.syntax.bicep | 4 +- .../baselines/Imports_LF/main.tokens.bicep | 4 +- .../Imports/parameters.syntax.bicepparam | 2 +- .../Imports/parameters.tokens.bicepparam | 2 +- .../parameters.syntax.bicepparam | 2 +- .../parameters.tokens.bicepparam | 2 +- .../PreferUnquotedPropertyNamesRuleTests.cs | 277 ++++++++++-------- .../Rules/PreferUnquotedPropertyNamesRule.cs | 8 +- src/Bicep.Core/LanguageConstants.cs | 8 +- src/Bicep.Core/Parsing/BaseParser.cs | 11 +- src/Bicep.Core/Parsing/Lexer.cs | 2 +- src/Bicep.Core/Parsing/Parser.cs | 16 +- src/Bicep.Core/Parsing/TokenType.cs | 2 - .../PrettyPrint/DocumentBuildVisitor.cs | 2 +- src/Bicep.Core/Syntax/AliasAsClauseSyntax.cs | 2 +- .../Syntax/ProviderWithClauseSyntax.cs | 2 +- src/Bicep.Core/Syntax/SyntaxFacts.cs | 2 - .../Completions/BicepCompletionContext.cs | 2 +- .../Completions/BicepCompletionProvider.cs | 2 +- .../ImportKubernetesManifestHandler.cs | 3 +- 21 files changed, 190 insertions(+), 167 deletions(-) diff --git a/src/Bicep.Core.IntegrationTests/ProviderImportTests.cs b/src/Bicep.Core.IntegrationTests/ProviderImportTests.cs index f3e7cd11dec..f98b6a42ae0 100644 --- a/src/Bicep.Core.IntegrationTests/ProviderImportTests.cs +++ b/src/Bicep.Core.IntegrationTests/ProviderImportTests.cs @@ -111,7 +111,7 @@ provider kubernetes with { } something """); result.Should().HaveDiagnostics(new[] { - ("BCP012", DiagnosticLevel.Error, "Expected the \"as\" keyword at this location."), + ("BCP305", DiagnosticLevel.Error, """Expected the "with" keyword, "as" keyword, or a new line character at this location."""), }); } diff --git a/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.syntax.bicep b/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.syntax.bicep index e875df2099b..c6d72e42173 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.syntax.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.syntax.bicep @@ -31,7 +31,7 @@ import * as mod2 from 'modules/mod2.bicep' //@[07:016) | ├─WildcardImportSyntax //@[07:008) | | ├─Token(Asterisk) |*| //@[09:016) | | └─AliasAsClauseSyntax -//@[09:011) | | ├─Token(AsKeyword) |as| +//@[09:011) | | ├─Token(Identifier) |as| //@[12:016) | | └─IdentifierSyntax //@[12:016) | | └─Token(Identifier) |mod2| //@[17:042) | └─CompileTimeImportFromClauseSyntax @@ -50,7 +50,7 @@ import { //@[02:032) | | | ├─StringSyntax //@[02:032) | | | | └─Token(StringComplete) |'not-a-valid-bicep-identifier'| //@[33:057) | | | └─AliasAsClauseSyntax -//@[33:035) | | | ├─Token(AsKeyword) |as| +//@[33:035) | | | ├─Token(Identifier) |as| //@[36:057) | | | └─IdentifierSyntax //@[36:057) | | | └─Token(Identifier) |withInvalidIdentifier| //@[57:058) | | ├─Token(NewLine) |\n| diff --git a/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.tokens.bicep b/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.tokens.bicep index b79b64e67ff..e2edf807072 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.tokens.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.tokens.bicep @@ -15,7 +15,7 @@ import {foo, fizz, pop, greet} from 'modules/mod.bicep' import * as mod2 from 'modules/mod2.bicep' //@[00:06) Identifier |import| //@[07:08) Asterisk |*| -//@[09:11) AsKeyword |as| +//@[09:11) Identifier |as| //@[12:16) Identifier |mod2| //@[17:21) Identifier |from| //@[22:42) StringComplete |'modules/mod2.bicep'| @@ -26,7 +26,7 @@ import { //@[08:09) NewLine |\n| 'not-a-valid-bicep-identifier' as withInvalidIdentifier //@[02:32) StringComplete |'not-a-valid-bicep-identifier'| -//@[33:35) AsKeyword |as| +//@[33:35) Identifier |as| //@[36:57) Identifier |withInvalidIdentifier| //@[57:58) NewLine |\n| refersToCopyVariable diff --git a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.syntax.bicepparam b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.syntax.bicepparam index ebc4c63777a..1831c3a3e81 100644 --- a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.syntax.bicepparam +++ b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.syntax.bicepparam @@ -11,7 +11,7 @@ import * as bicepconfig from 'bicepconfig.bicep' //@[007:023) | ├─WildcardImportSyntax //@[007:008) | | ├─Token(Asterisk) |*| //@[009:023) | | └─AliasAsClauseSyntax -//@[009:011) | | ├─Token(AsKeyword) |as| +//@[009:011) | | ├─Token(Identifier) |as| //@[012:023) | | └─IdentifierSyntax //@[012:023) | | └─Token(Identifier) |bicepconfig| //@[024:048) | └─CompileTimeImportFromClauseSyntax diff --git a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.tokens.bicepparam b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.tokens.bicepparam index c4b5534c65d..d18c76b3919 100644 --- a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.tokens.bicepparam +++ b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Imports/parameters.tokens.bicepparam @@ -5,7 +5,7 @@ using 'main.bicep' import * as bicepconfig from 'bicepconfig.bicep' //@[000:006) Identifier |import| //@[007:008) Asterisk |*| -//@[009:011) AsKeyword |as| +//@[009:011) Identifier |as| //@[012:023) Identifier |bicepconfig| //@[024:028) Identifier |from| //@[029:048) StringComplete |'bicepconfig.bicep'| diff --git a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.syntax.bicepparam b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.syntax.bicepparam index 77856e9fa66..2d3570eb22f 100644 --- a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.syntax.bicepparam +++ b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.syntax.bicepparam @@ -12,7 +12,7 @@ import * as foo from 'foo.bicep' //@[07:15) | ├─WildcardImportSyntax //@[07:08) | | ├─Token(Asterisk) |*| //@[09:15) | | └─AliasAsClauseSyntax -//@[09:11) | | ├─Token(AsKeyword) |as| +//@[09:11) | | ├─Token(Identifier) |as| //@[12:15) | | └─IdentifierSyntax //@[12:15) | | └─Token(Identifier) |foo| //@[16:32) | └─CompileTimeImportFromClauseSyntax diff --git a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.tokens.bicepparam b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.tokens.bicepparam index 944b9c13d82..ad6a4aaac47 100644 --- a/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.tokens.bicepparam +++ b/src/Bicep.Core.Samples/Files/baselines_bicepparam/Invalid_Imports/parameters.tokens.bicepparam @@ -6,7 +6,7 @@ using 'main.bicep' import * as foo from 'foo.bicep' //@[00:06) Identifier |import| //@[07:08) Asterisk |*| -//@[09:11) AsKeyword |as| +//@[09:11) Identifier |as| //@[12:15) Identifier |foo| //@[16:20) Identifier |from| //@[21:32) StringComplete |'foo.bicep'| diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/PreferUnquotedPropertyNamesRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/PreferUnquotedPropertyNamesRuleTests.cs index 6e054f8172a..9cbfeda2fab 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/PreferUnquotedPropertyNamesRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/PreferUnquotedPropertyNamesRuleTests.cs @@ -7,144 +7,165 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests +namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests; + +[TestClass] +public class PreferUnquotedPropertyNamesRuleTests : LinterRuleTestsBase { - [TestClass] - public class PreferUnquotedPropertyNamesRuleTests : LinterRuleTestsBase + private void AssertNoDiagnostics(string inputFile) + => AssertLinterRuleDiagnostics(PreferUnquotedPropertyNamesRule.Code, inputFile, [], new(OnCompileErrors.Ignore, IncludePosition.None)); + + private void ExpectPass(string text) { - private void ExpectPass(string text) - { - AssertLinterRuleDiagnostics(PreferUnquotedPropertyNamesRule.Code, text, []); - } + AssertLinterRuleDiagnostics(PreferUnquotedPropertyNamesRule.Code, text, []); + } - private void ExpectDiagnosticWithFix(string text, string expectedFix) + private void ExpectDiagnosticWithFix(string text, string expectedFix) + { + AssertLinterRuleDiagnostics(PreferUnquotedPropertyNamesRule.Code, text, diags => { - AssertLinterRuleDiagnostics(PreferUnquotedPropertyNamesRule.Code, text, diags => - { - diags.Should().HaveCount(1, $"expected one fix per testcase"); + diags.Should().HaveCount(1, $"expected one fix per testcase"); - diags.First().As().Fixes.Should().HaveCount(1); - diags.First().As().Fixes.First().Replacements.Should().HaveCount(1); - diags.First().As().Fixes.First().Replacements.First().Text.Should().Be(expectedFix); - }); - } + diags.First().As().Fixes.Should().HaveCount(1); + diags.First().As().Fixes.First().Replacements.Should().HaveCount(1); + diags.First().As().Fixes.First().Replacements.First().Text.Should().Be(expectedFix); + }); + } - [DataRow( - @" - var v1 = { - 'myProp': {} - }", - "myProp" - )] - [DataRow( - @" - var v1 = { - 'my_property': {} - }", - "my_property" - )] - [DataRow( - @" - var v1 = { - 'myProp1': {} - }", - "myProp1" - )] - [DataTestMethod] - public void ObjectPropertyDeclaration(string text, string expectedFix) - { - ExpectDiagnosticWithFix(text, expectedFix); - } + [DataRow( + @" + var v1 = { + 'myProp': {} + }", + "myProp" + )] + [DataRow( + @" + var v1 = { + 'my_property': {} + }", + "my_property" + )] + [DataRow( + @" + var v1 = { + 'myProp1': {} + }", + "myProp1" + )] + [DataTestMethod] + public void ObjectPropertyDeclaration(string text, string expectedFix) + { + ExpectDiagnosticWithFix(text, expectedFix); + } - [DataRow( - @" - var v1 = { - '1': {} - }" - )] - [DataRow( - @" - var v1 = { - 'my-property': {} - }" - )] - [DataTestMethod] - public void ObjectPropertyDeclaration_NotValidIdentifier(string text) - { - ExpectPass(text); - } + [DataRow( + @" + var v1 = { + '1': {} + }" + )] + [DataRow( + @" + var v1 = { + 'my-property': {} + }" + )] + [DataTestMethod] + public void ObjectPropertyDeclaration_NotValidIdentifier(string text) + { + ExpectPass(text); + } - [DataRow( - @" - var v1 = { - v1: {} - }" - )] - [DataRow( - @" - var v1 = { - myProperty: {} - }" - )] - [DataTestMethod] - public void ObjectPropertyDeclaration_AlreadyBare(string text) - { - ExpectPass(text); - } + [DataRow( + @" + var v1 = { + v1: {} + }" + )] + [DataRow( + @" + var v1 = { + myProperty: {} + }" + )] + [DataTestMethod] + public void ObjectPropertyDeclaration_AlreadyBare(string text) + { + ExpectPass(text); + } - [DataRow( - @" - param AnObject object - var v1 = AnObject['myProp']", - ".myProp" - )] - [DataRow( - @" - param AnObject object - var v1 = AnObject['my_property']", - ".my_property" - )] - [DataRow( - @" - param AnObject object - var v1 = AnObject['myProp1']", - ".myProp1" - )] - [DataTestMethod] - public void ObjectPropertyDereference(string text, string expectedFix) - { - ExpectDiagnosticWithFix(text, expectedFix); - } + [DataRow( + @" + param AnObject object + var v1 = AnObject['myProp']", + ".myProp" + )] + [DataRow( + @" + param AnObject object + var v1 = AnObject['my_property']", + ".my_property" + )] + [DataRow( + @" + param AnObject object + var v1 = AnObject['myProp1']", + ".myProp1" + )] + [DataTestMethod] + public void ObjectPropertyDereference(string text, string expectedFix) + { + ExpectDiagnosticWithFix(text, expectedFix); + } - [DataRow( - @" - param AnObject object - var v1 = AnObject['1']" - )] - [DataRow( - @" - param AnObject object - var v1 = AnObject['my-property']" - )] - [DataTestMethod] - public void ObjectPropertyDereference_NotValidIdentifier(string text) - { - ExpectPass(text); - } + [DataRow( + @" + param AnObject object + var v1 = AnObject['1']" + )] + [DataRow( + @" + param AnObject object + var v1 = AnObject['my-property']" + )] + [DataTestMethod] + public void ObjectPropertyDereference_NotValidIdentifier(string text) + { + ExpectPass(text); + } - [DataRow( - @" - param AutomationAccountName string - param AutomationAccountLocation string - resource AutomationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' = { - name: AutomationAccountName - location: AutomationAccountLocation - }" - )] - [DataTestMethod] - public void NoPropertyDeclarationOrDereference_Passes(string text) - { - ExpectPass(text); - } + [DataRow( + @" + param AutomationAccountName string + param AutomationAccountLocation string + resource AutomationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' = { + name: AutomationAccountName + location: AutomationAccountLocation + }" + )] + [DataTestMethod] + public void NoPropertyDeclarationOrDereference_Passes(string text) + { + ExpectPass(text); } + + [TestMethod] + public void Rule_ignores_non_contextual_keywords_in_object_keys() => AssertNoDiagnostics(""" +var test = { + 'null': 'asdf' + 'true': 'asdf' + 'false': 'asdf' } +"""); + + [TestMethod] + public void Rule_ignores_non_contextual_keywords_in_object_property_access() => AssertNoDiagnostics(""" +param test object +var foo = [ + test['null'] + test['true'] + test['false'] +] +"""); +} \ No newline at end of file diff --git a/src/Bicep.Core/Analyzers/Linter/Rules/PreferUnquotedPropertyNamesRule.cs b/src/Bicep.Core/Analyzers/Linter/Rules/PreferUnquotedPropertyNamesRule.cs index 41c113cf20a..d5be508ce07 100644 --- a/src/Bicep.Core/Analyzers/Linter/Rules/PreferUnquotedPropertyNamesRule.cs +++ b/src/Bicep.Core/Analyzers/Linter/Rules/PreferUnquotedPropertyNamesRule.cs @@ -70,10 +70,12 @@ private void AddCodeFix(TextSpan span, string replacement, string description) private static bool TryGetValidIdentifierToken(SyntaxBase syntax, [NotNullWhen(true)] out string? validToken) { - if (syntax is StringSyntax @string) + if (syntax is StringSyntax stringSyntax && + stringSyntax.TryGetLiteralValue() is {} literalValue) { - string? literalValue = @string.TryGetLiteralValue(); - if (literalValue is not null && Lexer.IsValidIdentifier(literalValue)) + if (Lexer.IsValidIdentifier(literalValue) && + // exclude non-contextual keywords like 'nul and 'true' - see https://github.com/Azure/bicep/issues/13347. + !LanguageConstants.NonContextualKeywords.ContainsKey(literalValue)) { validToken = literalValue; return true; diff --git a/src/Bicep.Core/LanguageConstants.cs b/src/Bicep.Core/LanguageConstants.cs index 5a342102e44..1a3b7c6c8f3 100644 --- a/src/Bicep.Core/LanguageConstants.cs +++ b/src/Bicep.Core/LanguageConstants.cs @@ -117,7 +117,9 @@ public static class LanguageConstants .Add(IfKeyword) .Add(ForKeyword) .Add(InKeyword) - .Add(FromKeyword); + .Add(FromKeyword) + .Add(WithKeyword) + .Add(AsKeyword); public const string TrueKeyword = "true"; public const string FalseKeyword = "false"; @@ -128,13 +130,11 @@ public static class LanguageConstants public const string McrRepositoryPrefix = "bicep/"; - public static readonly ImmutableDictionary Keywords = new Dictionary(StringComparer.Ordinal) + public static readonly ImmutableDictionary NonContextualKeywords = new Dictionary(StringComparer.Ordinal) { [TrueKeyword] = TokenType.TrueKeyword, [FalseKeyword] = TokenType.FalseKeyword, [NullKeyword] = TokenType.NullKeyword, - [WithKeyword] = TokenType.WithKeyword, - [AsKeyword] = TokenType.AsKeyword, }.ToImmutableDictionary(); // Decorators diff --git a/src/Bicep.Core/Parsing/BaseParser.cs b/src/Bicep.Core/Parsing/BaseParser.cs index b4da42773cb..ba0a1499b13 100644 --- a/src/Bicep.Core/Parsing/BaseParser.cs +++ b/src/Bicep.Core/Parsing/BaseParser.cs @@ -304,10 +304,11 @@ protected Token Expect(TokenType type, DiagnosticBuilder.ErrorBuilderDelegate er throw new ExpectedTokenException(this.reader.Peek(), errorFunc); } - protected Token ExpectKeyword(string expectedKeyword) + protected Token ExpectKeyword(string expectedKeyword, DiagnosticBuilder.ErrorBuilderDelegate? errorFunc = null) { + errorFunc ??= b => b.ExpectedKeyword(expectedKeyword); return GetOptionalKeyword(expectedKeyword) ?? - throw new ExpectedTokenException(this.reader.Peek(), b => b.ExpectedKeyword(expectedKeyword)); + throw new ExpectedTokenException(this.reader.Peek(), errorFunc); } private SyntaxBase ForBody(ExpressionFlags expressionFlags, bool isResourceOrModuleContext) @@ -1712,13 +1713,13 @@ private SyntaxBase ImportedSymbolsListItem() return new ImportedSymbolsListItemSyntax(originalSymbolName, aliasAsClause); } - private AliasAsClauseSyntax? ImportedSymbolsListItemAsClause() => Check(reader.Peek(), TokenType.AsKeyword) - ? new(Expect(TokenType.AsKeyword, b => b.ExpectedKeyword(LanguageConstants.AsKeyword)), + private AliasAsClauseSyntax? ImportedSymbolsListItemAsClause() => CheckKeyword(reader.Peek(), LanguageConstants.AsKeyword) + ? new(ExpectKeyword(LanguageConstants.AsKeyword), IdentifierWithRecovery(b => b.ExpectedTypeIdentifier(), RecoveryFlags.None, TokenType.Comma, TokenType.NewLine)) : null; private WildcardImportSyntax WildcardImport() => new(Expect(TokenType.Asterisk, b => b.ExpectedCharacter("*")), - new AliasAsClauseSyntax(Expect(TokenType.AsKeyword, b => b.ExpectedKeyword(LanguageConstants.AsKeyword)), + new AliasAsClauseSyntax(ExpectKeyword(LanguageConstants.AsKeyword), Identifier(b => b.ExpectedNamespaceIdentifier()))); private CompileTimeImportFromClauseSyntax CompileTimeImportFromClause() diff --git a/src/Bicep.Core/Parsing/Lexer.cs b/src/Bicep.Core/Parsing/Lexer.cs index 5936c841293..e9c166e9527 100644 --- a/src/Bicep.Core/Parsing/Lexer.cs +++ b/src/Bicep.Core/Parsing/Lexer.cs @@ -820,7 +820,7 @@ private TokenType GetIdentifierTokenType() { var identifier = textWindow.GetText().ToString(); - if (LanguageConstants.Keywords.TryGetValue(identifier, out var tokenType)) + if (LanguageConstants.NonContextualKeywords.TryGetValue(identifier, out var tokenType)) { return tokenType; } diff --git a/src/Bicep.Core/Parsing/Parser.cs b/src/Bicep.Core/Parsing/Parser.cs index 05a637e921a..79c216a9faa 100644 --- a/src/Bicep.Core/Parsing/Parser.cs +++ b/src/Bicep.Core/Parsing/Parser.cs @@ -292,16 +292,18 @@ private ProviderDeclarationSyntax ProviderImportDeclaration(Token keyword, IEnum TokenType.NewLine) }; - var withClause = this.reader.Peek().Type switch + var current = this.reader.Peek(); + var withClause = current.Type switch { TokenType.EndOfFile or - TokenType.NewLine or - TokenType.AsKeyword => this.SkipEmpty(), + TokenType.NewLine => this.SkipEmpty(), + TokenType.Identifier when current.Text == LanguageConstants.AsKeyword => this.SkipEmpty(), _ => this.WithRecovery(() => this.ProviderWithClause(), GetSuppressionFlag(providerSpecificationSyntax), TokenType.NewLine), }; - var asClause = this.reader.Peek().Type switch + current = this.reader.Peek(); + var asClause = current.Type switch { TokenType.EndOfFile or TokenType.NewLine => this.SkipEmpty(), @@ -314,15 +316,15 @@ TokenType.EndOfFile or private ProviderWithClauseSyntax ProviderWithClause() { - var keyword = this.Expect(TokenType.WithKeyword, b => b.ExpectedWithOrAsKeywordOrNewLine()); - var config = this.WithRecovery(() => this.Object(ExpressionFlags.AllowComplexLiterals), RecoveryFlags.None, TokenType.AsKeyword, TokenType.NewLine); + var keyword = this.ExpectKeyword(LanguageConstants.WithKeyword, b => b.ExpectedWithOrAsKeywordOrNewLine()); + var config = this.WithRecovery(() => this.Object(ExpressionFlags.AllowComplexLiterals), RecoveryFlags.None, TokenType.NewLine); return new(keyword, config); } private AliasAsClauseSyntax ProviderAsClause() { - var keyword = this.Expect(TokenType.AsKeyword, b => b.ExpectedKeyword(LanguageConstants.AsKeyword)); + var keyword = this.ExpectKeyword(LanguageConstants.AsKeyword, b => b.ExpectedWithOrAsKeywordOrNewLine()); var modifier = this.IdentifierWithRecovery(b => b.ExpectedProviderAliasName(), RecoveryFlags.None, TokenType.NewLine); return new(keyword, modifier); diff --git a/src/Bicep.Core/Parsing/TokenType.cs b/src/Bicep.Core/Parsing/TokenType.cs index 1b9c6e6e9fa..5b0c5372da6 100644 --- a/src/Bicep.Core/Parsing/TokenType.cs +++ b/src/Bicep.Core/Parsing/TokenType.cs @@ -50,8 +50,6 @@ public enum TokenType DoubleColon, Arrow, Pipe, - WithKeyword, - AsKeyword, Ellipsis, } } diff --git a/src/Bicep.Core/PrettyPrint/DocumentBuildVisitor.cs b/src/Bicep.Core/PrettyPrint/DocumentBuildVisitor.cs index bdcb4efc9c7..3dc87233f3c 100644 --- a/src/Bicep.Core/PrettyPrint/DocumentBuildVisitor.cs +++ b/src/Bicep.Core/PrettyPrint/DocumentBuildVisitor.cs @@ -26,7 +26,7 @@ public class DocumentBuildVisitor : CstVisitor private static readonly ImmutableDictionary CommonTextCache = LanguageConstants.ContextualKeywords - .Concat(LanguageConstants.Keywords.Keys) + .Concat(LanguageConstants.NonContextualKeywords.Keys) .Concat(["(", ")", "[", "]", "{", "}", "=", ":", "+", "-", "*", "/", "!"]) .Concat(["name", "properties", "string", "bool", "int", "array", "object"]) .ToImmutableDictionary(value => value, value => new TextDocument(value)); diff --git a/src/Bicep.Core/Syntax/AliasAsClauseSyntax.cs b/src/Bicep.Core/Syntax/AliasAsClauseSyntax.cs index b580bd67970..81ab6a3467e 100644 --- a/src/Bicep.Core/Syntax/AliasAsClauseSyntax.cs +++ b/src/Bicep.Core/Syntax/AliasAsClauseSyntax.cs @@ -9,7 +9,7 @@ public class AliasAsClauseSyntax : SyntaxBase { public AliasAsClauseSyntax(Token keyword, IdentifierSyntax alias) { - AssertTokenType(keyword, nameof(keyword), TokenType.AsKeyword); + AssertKeyword(keyword, nameof(keyword), LanguageConstants.AsKeyword); AssertSyntaxType(alias, nameof(alias), typeof(IdentifierSyntax), typeof(SkippedTriviaSyntax)); this.Keyword = keyword; diff --git a/src/Bicep.Core/Syntax/ProviderWithClauseSyntax.cs b/src/Bicep.Core/Syntax/ProviderWithClauseSyntax.cs index e35522b6559..f34a37a952d 100644 --- a/src/Bicep.Core/Syntax/ProviderWithClauseSyntax.cs +++ b/src/Bicep.Core/Syntax/ProviderWithClauseSyntax.cs @@ -9,7 +9,7 @@ public class ProviderWithClauseSyntax : SyntaxBase { public ProviderWithClauseSyntax(Token keyword, SyntaxBase config) { - AssertTokenType(keyword, nameof(keyword), TokenType.WithKeyword); + AssertKeyword(keyword, nameof(keyword), LanguageConstants.WithKeyword); AssertSyntaxType(config, nameof(config), typeof(ObjectSyntax), typeof(SkippedTriviaSyntax)); this.Keyword = keyword; diff --git a/src/Bicep.Core/Syntax/SyntaxFacts.cs b/src/Bicep.Core/Syntax/SyntaxFacts.cs index 6161d4a58db..f6fffefac48 100644 --- a/src/Bicep.Core/Syntax/SyntaxFacts.cs +++ b/src/Bicep.Core/Syntax/SyntaxFacts.cs @@ -61,8 +61,6 @@ TokenType.MultilineString or TokenType.DoubleColon => "::", TokenType.Arrow => "=>", TokenType.Pipe => "|", - TokenType.WithKeyword => "with", - TokenType.AsKeyword => "as", TokenType.Ellipsis => "...", _ => null, }; diff --git a/src/Bicep.LangServer/Completions/BicepCompletionContext.cs b/src/Bicep.LangServer/Completions/BicepCompletionContext.cs index 3a21be48ec0..028eb224277 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionContext.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionContext.cs @@ -49,7 +49,7 @@ public record IndexedSyntaxContext(T Syntax, int ArgumentIndex) where T : Syn TokenType.Identifier, TokenType.Integer, TokenType.StringComplete, - .. LanguageConstants.Keywords.Values, + .. LanguageConstants.NonContextualKeywords.Values, ]; private BicepCompletionContext( diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index c4e5f48012e..2dc51a6a767 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -2174,7 +2174,7 @@ private static CompletionItemKind GetCompletionItemKind(SymbolKind symbolKind) = }; private static bool IsPropertyNameEscapingRequired(TypeProperty property) => - !Lexer.IsValidIdentifier(property.Name) || LanguageConstants.Keywords.ContainsKey(property.Name); + !Lexer.IsValidIdentifier(property.Name) || LanguageConstants.NonContextualKeywords.ContainsKey(property.Name); private static string FormatPropertyDetail(TypeProperty property) => TypeHelper.IsRequired(property) diff --git a/src/Bicep.LangServer/Handlers/ImportKubernetesManifestHandler.cs b/src/Bicep.LangServer/Handlers/ImportKubernetesManifestHandler.cs index 3dc8943981d..43dfc189df2 100644 --- a/src/Bicep.LangServer/Handlers/ImportKubernetesManifestHandler.cs +++ b/src/Bicep.LangServer/Handlers/ImportKubernetesManifestHandler.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Text; +using Bicep.Core; using Bicep.Core.Configuration; using Bicep.Core.Diagnostics; using Bicep.Core.FileSystem; @@ -64,7 +65,7 @@ public string Decompile(string bicepFilePath, string manifestContents, Telemetry SyntaxFactory.ProviderKeywordToken, SyntaxFactory.CreateIdentifierWithTrailingSpace(K8sNamespaceType.BuiltInName), new ProviderWithClauseSyntax( - SyntaxFactory.CreateToken(TokenType.WithKeyword), + SyntaxFactory.CreateIdentifierToken(LanguageConstants.WithKeyword), SyntaxFactory.CreateObject( [ SyntaxFactory.CreateObjectProperty("namespace", SyntaxFactory.CreateStringLiteral("default")),