Skip to content

Conversation

@jeskew
Copy link
Member

@jeskew jeskew commented Dec 5, 2025

Description

This PR adds two new pragmas/directives, #disable-diagnostics and #restore-diagnostics. The former can be used to disable diagnostics by code for an entire file, or, when paired with #restore-diagnostics, for code that falls between the directives.

As with #disable-next-line, these pragmas will not suppress error-level diagnostics. Passing a code that has not been disabled to #restore-diagnostics is a no-op, as is passing a non-existent diagnostic code (to match the behavior of #disable-next-line).

Example Usage

Disable a specific diagnostic for a whole file

#disable-diagnostics BCP422

...

resource foo 'type@version' = if (condition) {}

output bar string = foo.properties.bar // <-- would normally raise BCP422 as a warning, but will not due to pragma at top of file

Disable specific diagnostics for a span

resource foo 'type@version' = if (condition) {}

#disable-diagnostics BCP422
output bar string = foo.properties.bar // <-- would normally raise BCP422 as a warning, but will not due to pragma at top of file
#restore-diagnostics BCP422

output baz string = foo.properties.baz // <-- will raise BCP422 warning because it's outside of the pragma fence
Microsoft Reviewers: Open in CodeFlow

@github-actions
Copy link
Contributor

github-actions bot commented Dec 5, 2025

Test this change out locally with the following install scripts (Action run 19978233671)

VSCode
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-vsix.sh) --run-id 19978233671
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-vsix.ps1) } -RunId 19978233671"
Azure CLI
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-cli.sh) --run-id 19978233671
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-cli.ps1) } -RunId 19978233671"

@github-actions
Copy link
Contributor

github-actions bot commented Dec 5, 2025

Dotnet Test Results

   102 files   -     51     102 suites   - 51   38m 41s ⏱️ - 23m 55s
12 576 tests  -      9  12 576 ✅  -      9  0 💤 ±0  0 ❌ ±0 
28 897 runs   - 14 430  28 897 ✅  - 14 430  0 💤 ±0  0 ❌ ±0 

Results for commit ad0e86f. ± Comparison against base commit 8d3ffa5.

This pull request removes 1958 and adds 665 tests. Note that renamed tests count towards both.

		nestedProp1: 1
		nestedProp2: 2
		prop1: true
		prop2: false
	1
	2
	\$'")
	prop1: true
	prop2: false
…
Bicep.Cli.UnitTests.Services.ReplEnvironmentTests ‑ ShouldSubmitBuffer_terminates_at_expected_point ("var foo = {
")
Bicep.Cli.UnitTests.Services.ReplEnvironmentTests ‑ ShouldSubmitBuffer_terminates_at_expected_point ("var multilineString = '''
Line 1
Line 2
Line 3
'''")
Bicep.Cli.UnitTests.Services.ReplEnvironmentTests ‑ ShouldSubmitBuffer_terminates_at_expected_point ("var outRoleAssignments object[] = union(map(
  filter(varMockedEntraGroupIds, item => !contains(item.uniqueName, 'DevOps')),
  group => {
    principalId: group.groupId
    definition: group.roleToAssign
    relativeScope: ''
    principalType: 'Group'
  }
),[
  {
    principalId: '22222222-2222-2222-2222-222222222222'
    definition: 'Reader'
    relativeScope: ''
    principalType: 'ServicePrincipal'
  }
])")
Bicep.Cli.UnitTests.Services.ReplEnvironmentTests ‑ ShouldSubmitBuffer_terminates_at_expected_point ("var test = {
  abc: 'def' // boo
}")
Bicep.Cli.UnitTests.Services.ReplEnvironmentTests ‑ ShouldSubmitBuffer_terminates_at_expected_point ("var varMockedEntraGroupIds = [
  {
    uniqueName: 'Reader-Group'
    roleToAssign: 'Reader'
    groupId: '11111111-1111-1111-1111-111111111111'
  }
  {
    uniqueName: 'Contributor-Group'
    roleToAssign: 'Contributor'
    groupId: '22222222-2222-2222-2222-222222222222'
  }
  {
    uniqueName: 'DevOps-Group'
    groupId: '33333333-3333-3333-3333-333333333333'
  }
]")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000
�ӻ
�@\u0010\u0005Щ�
�`��O��O�_X��\u0007��\u0007\u0011B�=�*ؤ1Z����\u0014\u0017�+�c�\u000eu8�]/���\u0010�4\u0000�Z�\u0012�<\u0001˳{���O���\u001fBG\u0000���\u001e�s5�[l\u001fQ\�6f�̛���\u0015;��3
^(]zW�l��ɢ��N��s��ĆY[ˊ
��\u001cS��&�}���ںA�$IB\u001bx\u0003\u0005���\u0000\u000c\u0000\u0000,"The path: index.json was not found in artifact contents")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000
�Ա
�@\u000c\u0006��>��\u0007�&���
\u001d\u0004\u0007\u0015���*\u0007=�B��
\u0005_^t\u0011��T\u0005�o��\u000c��G�+�N��ݩ�Z%&��7DD��=\u0011����;��h@��\u0001纱'@��D�8�f�\u0016U�Z��\u000f� \u001a��)J�\u0012\u001b���1��a&Ão����qwx����\u0014�&R�PL\u001a�n?\u0001�G:y���_�Y�\u0019/f��|�̂�`~~ƞ�y\u001e��+��\u001e{\u0000\u000c\u0000\u0000,"'7' is an invalid end of a number. Expected a delimiter. Path: $.INVALID_JSON | LineNumber: 0 | BytePositionInLine: 20.")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000
��=
�@\u0010\u0005�=��\u0001�\u0019��\u0015�m\u0004\u001b\u000f��\u0011#&�$�xw�FHc�h���������l\u001b�C�u#��v&�o��V�w"b7\u0011���;�\u0016D�\u001f�5m�\u0001\u0011Ɖ��B{Z�e�Q��k9�/D��\u0005��Y�Q��s�4M�}nҳ��a0_��4�!�֒"\u0003H�\u0005\u0010?��m������X��l�Ş��R\u001cå�ǿoK�$I`0O��\�\u0000\u000c\u0000\u0000,"Value cannot be null. (Parameter 'source')")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0003�ӻ
�@\u0010\u0005Щ�\u0015K>`��ه\u0011��\u00086~�jFTL\u000cy@@�w�FHc�pOs�).\u000cW��a\r(��d\u0006�g��
\u0000�1�\u00040M��ɽwޒ\u0018�\u000b��\u000f-\u0001��T.��\u001f�c]�(OݥN�\T���Bygro5�̵�^���u�7{��>���3C�*e�S�h�\u0002`H|e�����*�M�cû��\u0015W[nӅ؇sǷ_w��(��c�e7��\u0000\u000c\u0000\u0000,"Value cannot be null. (Parameter 'source')")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0003��=
�@\u0010\u0005�=Ş`}3�)�S�
\u000b\u0011�k�J���\u0007R\u0005�4F\u000b�k^3���\u0010�}\u001d����\u0005��5�
\u0000�1�\u00040M����;\u000fR#-`��p'��IX�B���x��#�K��LJ��禮�;��V �t�"6[�p2��ӟ��?��2\u001b瘍\u0010\u0018��\u0016����?_k7H�$Ih\u0005o>&�\u0000\u000c\u0000\u0000,"The path: index.json was not found in artifact contents")
…

♻️ This comment has been updated with latest results.

@jeskew
Copy link
Member Author

jeskew commented Dec 5, 2025

NB for reviewers: I did not add much in terms of language server support for these new pragmas. The completions proposed for all diagnostic pragmas is the same, meaning that completions for #restore-diagnostics will include codes for diagnostics that have not been disabled. I also did not add a quick fix for disabling a given diagnostic for the whole file, since the current code action will add a #disable-next-line pragma, which won't affect unrelated lines. Please comment if you disagree with those decisions!

@jeskew jeskew marked this pull request as ready for review December 5, 2025 20:11
@jeskew jeskew requested review from a team and Copilot December 5, 2025 21:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces multiline diagnostic suppression directives (#disable-diagnostics and #restore-diagnostics) while refactoring the existing #disable-next-line directive to use a unified pragma system. The changes enable developers to disable specific diagnostics for entire files or for code spans between disable/restore pairs.

Key changes:

  • Unified all diagnostic pragmas under a single DiagnosticsPragmaSyntaxTrivia class with a DiagnosticsPragmaType enum
  • Implemented span-based caching in DisabledDiagnosticsCache to track disabled diagnostic regions
  • Updated lexer to recognize all three pragma types using dynamic keyword matching

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Bicep.Core/Syntax/DisableNextLineDiagnosticsSyntaxTrivia.cs Removed - replaced by unified DiagnosticsPragmaSyntaxTrivia
src/Bicep.Core/Syntax/DiagnosticsPragmaSyntaxTrivia.cs New unified class for all diagnostic pragmas with enum for pragma types
src/Bicep.Core/Syntax/SyntaxTriviaType.cs Renamed enum value from DisableNextLineDiagnosticsDirective to DiagnosticsPragma
src/Bicep.Core/Syntax/SyntaxFacts.cs Updated IsDirective check to use new pragma type
src/Bicep.Core/LanguageConstants.cs Added new keyword constants for disable-diagnostics and restore-diagnostics
src/Bicep.Core/Parsing/Lexer.cs Updated to dynamically detect pragma types and scan accordingly
src/Bicep.Core/Diagnostics/DisabledDiagnosticsCache.cs Completely rewritten to support span-based caching for all pragma types
src/Bicep.Core/Semantics/SemanticModel.cs Simplified diagnostic filtering to use new IsDisabledAtPosition method
src/Bicep.Core/PrettyPrintV2/SyntaxLayouts.cs Updated to format all pragma types dynamically
src/Bicep.Core/PrettyPrint/DocumentBuildVisitor.cs Updated trivia type check for pragma handling
src/Bicep.Core/Highlighting/SemanticTokenVisitor.cs Updated to handle unified pragma type
src/Bicep.LangServer/Completions/BicepCompletionContext.cs Updated type checks for pragma completion context
src/Bicep.Core.UnitTests/Parsing/LexerTests.cs Updated test assertions for new pragma type
src/Bicep.Core.IntegrationTests/DirectiveTests.cs New comprehensive integration tests for all pragma types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants