Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Bicep.Core.UnitTests/BicepTestConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ public static class BicepTestConstants
public static readonly ITemplateSpecRepositoryFactory TemplateSpecRepositoryFactory = StrictMock.Of<ITemplateSpecRepositoryFactory>().Object;

// Linter rules added to this list will be automatically disabled for most tests.
public static readonly string[] NonStableAnalyzerRules = [UseRecentApiVersionRule.Code, UseRecentModuleVersionsRule.Code, NoHardcodedOutputsRule.Code];
public static readonly string[] NonStableAnalyzerRules = [UseRecentApiVersionRule.Code, UseRecentModuleVersionsRule.Code, NoHardcodedOutputsRule.Code, UseParameterDescriptionsRule.Code];

// Rules that are currently skipped due to configuration for ProgramsShouldProduceExpectedDiagnostics
public static readonly string[] TestAnalyzersToSkip = [UseRecentApiVersionRule.Code, UseRecentModuleVersionsRule.Code, NoHardcodedLocationRule.Code, ExplicitValuesForLocationParamsRule.Code, NoLocationExprOutsideParamsRule.Code, NoModuleNameRule.Code, NoHardcodedOutputsRule.Code];
public static readonly string[] TestAnalyzersToSkip = [UseRecentApiVersionRule.Code, UseRecentModuleVersionsRule.Code, NoHardcodedLocationRule.Code, ExplicitValuesForLocationParamsRule.Code, NoLocationExprOutsideParamsRule.Code, NoModuleNameRule.Code, NoHardcodedOutputsRule.Code, UseParameterDescriptionsRule.Code];

public static readonly RootConfiguration BuiltInConfigurationWithAllAnalyzersDisabled = IConfigurationManager.GetBuiltInConfiguration().WithAllAnalyzersDisabled();
public static readonly RootConfiguration BuiltInConfigurationWithStableAnalyzers = IConfigurationManager.GetBuiltInConfiguration().WithAllAnalyzers().WithAnalyzersDisabled(NonStableAnalyzerRules);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Bicep.Core.Analyzers.Linter.Rules;
using Bicep.Core.Configuration;
using Bicep.Core.Extensions;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests;

[TestClass]
public class UseParameterDescriptionsRuleTests : LinterRuleTestsBase
{
private static readonly Options RuleOptions = new(ConfigurationPatch: EnableRule);

private static RootConfiguration EnableRule(RootConfiguration configuration) =>
configuration.WithAnalyzersConfiguration(
configuration.Analyzers.SetValue($"core.rules.{UseParameterDescriptionsRule.Code}.level", "warning"));

private void AssertDiagnostics(string inputFile, int expectedCount = 1)
=> AssertLinterRuleDiagnostics(UseParameterDescriptionsRule.Code, inputFile, expectedCount, RuleOptions);

private void AssertDiagnostics(string inputFile, string[] expectedMessages)
=> AssertLinterRuleDiagnostics(UseParameterDescriptionsRule.Code, inputFile, expectedMessages, RuleOptions);

private void AssertNoDiagnostics(string inputFile, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
=> AssertLinterRuleDiagnostics(
UseParameterDescriptionsRule.Code,
inputFile,
[],
RuleOptions with
{
OnCompileErrors = onCompileErrors,
IncludePosition = IncludePosition.None,
});

[TestMethod]
public void Rule_defaults_to_off()
{
var result = CompilationHelper.Compile("""
param input string
""");

result.ExcludingDiagnostics("no-unused-params").Should().NotHaveAnyDiagnostics();
}

[TestMethod]
public void Parameters_without_descriptions_are_reported()
{
AssertDiagnostics(
"""
param first string

@secure()
param second string
""",
[
"""[1] Parameter "first" must have a non-empty description.""",
"""[4] Parameter "second" must have a non-empty description.""",
]);
}

[DataRow("""
@description('Parameter description.')
param input string
""")]
[DataRow("""
@sys.description('Parameter description.')
param input string
""")]
[DataTestMethod]
public void Non_empty_descriptions_are_accepted(string text)
{
AssertNoDiagnostics(text);
}

[DataRow("""
@description('')
param input string
""")]
[DataRow("""
@description(' ')
param input string
""")]
[DataRow("""
@sys.description('')
param input string
""")]
[DataRow("""
@sys.description('''

''')
param input string
""")]
[DataTestMethod]
public void Empty_and_whitespace_descriptions_are_reported(string text)
{
AssertDiagnostics(text);
}

[TestMethod]
public void Metadata_description_does_not_satisfy_the_rule()
{
AssertDiagnostics(
"""
@metadata({ description: 'Metadata description.' })
param input string
""",
["""[2] Parameter "input" must have a non-empty description."""]);
}

[DataRow("""
@description('Variable description.')
var value = 'value'
""")]
[DataRow("""
@description('Output description.')
output result string = 'value'
""")]
[DataTestMethod]
public void Descriptions_on_other_declarations_are_ignored(string text)
{
AssertNoDiagnostics(text);
}

[TestMethod]
public void Malformed_parameter_without_a_name_is_ignored()
{
AssertNoDiagnostics("param", OnCompileErrors.Ignore);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ param condition bool
}
"""),
("mod3.bicep", """
param name string
param name string

resource vnet 'Microsoft.Network/virtualNetworks@2024-07-01' = {
name: name
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Bicep.Core.Diagnostics;
using Bicep.Core.Semantics;
using Bicep.Core.Semantics.Namespaces;

namespace Bicep.Core.Analyzers.Linter.Rules;

public sealed class UseParameterDescriptionsRule : LinterRuleBase
{
public new const string Code = "use-parameter-descriptions";

public UseParameterDescriptionsRule() : base(
code: Code,
description: CoreResources.UseParameterDescriptionsRuleDescription,
LinterRuleCategory.BestPractice,
overrideCategoryDefaultDiagnosticLevel: DiagnosticLevel.Off)
{ }

public override string FormatMessage(params object[] values)
=> string.Format(CoreResources.UseParameterDescriptionsRuleMessageFormat, values);

public override IEnumerable<IDiagnostic> AnalyzeInternal(SemanticModel model, DiagnosticLevel diagnosticLevel)
{
foreach (var parameter in model.Root.ParameterDeclarations.Where(parameter => parameter.NameSource.IsValid))
{
var descriptionDecorator = parameter.TryGetDecorator(
model,
SystemNamespaceType.BuiltInName,
LanguageConstants.MetadataDescriptionPropertyName);

if (descriptionDecorator is null)
{
yield return CreateDiagnosticForSpan(diagnosticLevel, parameter.NameSource.Span, parameter.Name);
continue;
}

if (DescriptionHelper.TryGetFromDecorator(model, parameter.DeclaringParameter) is { } description &&
string.IsNullOrWhiteSpace(description))
{
yield return CreateDiagnosticForSpan(
diagnosticLevel,
descriptionDecorator.Arguments.First().Expression.Span,
parameter.Name);
}
}
}
}
20 changes: 19 additions & 1 deletion src/Bicep.Core/CoreResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/Bicep.Core/CoreResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,13 @@
<data name="UseParentPropertyRule_CodeFix" xml:space="preserve">
<value>Use parent property</value>
</data>
<data name="UseParameterDescriptionsRule_Description" xml:space="preserve">
<value>Parameters should have non-empty descriptions.</value>
</data>
<data name="UseParameterDescriptionsRule_MessageFormat" xml:space="preserve">
<value>Parameter "{0}" must have a non-empty description.</value>
<comment>{0} is the parameter name</comment>
</data>
<data name="UseSecureValueForSecureInputsRule_MessageFormat" xml:space="preserve">
<value>Property '{0}' expects a secure value, but the value provided may not be secure.</value>
<comment>{0} property name</comment>
Expand Down
10 changes: 10 additions & 0 deletions src/vscode-bicep/schemas/bicepconfig.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,16 @@
}
]
},
"use-parameter-descriptions": {
"allOf": [
{
"description": "Parameters should have non-empty descriptions. Defaults to 'Off'. See https://aka.ms/bicep/linter-diagnostics#use-parameter-descriptions"
},
{
"$ref": "#/definitions/rule-def-level-off"
}
]
},
"use-safe-access": {
"allOf": [
{
Expand Down
Loading