-
Notifications
You must be signed in to change notification settings - Fork 756
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add use-safe-access linter rule (#14322)
Recommends against the following: ```bicep var test = contains(foo, 'bar') ? foo.bar : 'baz' ``` And instead suggests: ```bicep var test = foo.?bar ?? 'baz' ``` ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/14322)
- Loading branch information
1 parent
1a695a1
commit fece467
Showing
8 changed files
with
250 additions
and
38 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseSafeAccessRuleTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using Bicep.Core.Analyzers.Linter.Rules; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests; | ||
|
||
[TestClass] | ||
public class UseSafeAccessRuleTests : LinterRuleTestsBase | ||
{ | ||
private void AssertCodeFix(string inputFile, string resultFile) | ||
=> AssertCodeFix(UseSafeAccessRule.Code, "Use the safe access (.?) operator", inputFile, resultFile); | ||
|
||
private void AssertNoDiagnostics(string inputFile) | ||
=> AssertLinterRuleDiagnostics(UseSafeAccessRule.Code, inputFile, [], new(OnCompileErrors.Ignore, IncludePosition.None)); | ||
|
||
[TestMethod] | ||
public void Codefix_fixes_syntax_which_can_be_simplified() => AssertCodeFix(""" | ||
param foo object | ||
var test = contai|ns(foo, 'bar') ? foo.bar : 'baz' | ||
""", """ | ||
param foo object | ||
var test = foo.?bar ?? 'baz' | ||
"""); | ||
|
||
[TestMethod] | ||
public void Codefix_fixes_syntax_which_can_be_simplified_array_access() => AssertCodeFix(""" | ||
param foo object | ||
param target string | ||
var test = contai|ns(foo, target) ? foo[target] : 'baz' | ||
""", """ | ||
param foo object | ||
param target string | ||
var test = foo[?target] ?? 'baz' | ||
"""); | ||
|
||
[TestMethod] | ||
public void Rule_ignores_syntax_which_cannot_be_simplified() => AssertNoDiagnostics(""" | ||
param foo object | ||
var test = contains(foo, 'bar') ? foo.baz : 'baz' | ||
"""); | ||
|
||
[TestMethod] | ||
public void Rule_ignores_syntax_which_cannot_be_simplified_2() => AssertNoDiagnostics(""" | ||
param foo object | ||
param target string | ||
param notTarget string | ||
var test = contains(foo, target) ? bar[notTarget] : 'baz' | ||
"""); | ||
|
||
[TestMethod] | ||
public void Rule_ignores_syntax_which_cannot_be_simplified_array_access() => AssertNoDiagnostics(""" | ||
param foo object | ||
var test = contains(foo, 'bar') ? bar.bar : 'baz' | ||
"""); | ||
} |
63 changes: 63 additions & 0 deletions
63
src/Bicep.Core/Analyzers/Linter/Rules/UseSafeAccessRule.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using Bicep.Core.CodeAction; | ||
using Bicep.Core.Diagnostics; | ||
using Bicep.Core.Parsing; | ||
using Bicep.Core.Semantics; | ||
using Bicep.Core.Semantics.Namespaces; | ||
using Bicep.Core.Syntax; | ||
using Bicep.Core.Syntax.Comparers; | ||
using Bicep.Core.Syntax.Visitors; | ||
|
||
namespace Bicep.Core.Analyzers.Linter.Rules; | ||
|
||
public sealed class UseSafeAccessRule : LinterRuleBase | ||
{ | ||
public new const string Code = "use-safe-access"; | ||
|
||
public UseSafeAccessRule() : base( | ||
code: Code, | ||
description: CoreResources.UseSafeAccessRule_Description, | ||
LinterRuleCategory.BestPractice, | ||
docUri: new Uri($"https://aka.ms/bicep/linter/{Code}")) | ||
{ } | ||
|
||
public override IEnumerable<IDiagnostic> AnalyzeInternal(SemanticModel model, DiagnosticLevel diagnosticLevel) | ||
{ | ||
foreach (var ternary in SyntaxAggregator.AggregateByType<TernaryOperationSyntax>(model.Root.Syntax)) | ||
{ | ||
if (SemanticModelHelper.TryGetNamedFunction(model, SystemNamespaceType.BuiltInName, "contains", ternary.ConditionExpression) is not {} functionCall || | ||
functionCall.Arguments.Length != 2) | ||
{ | ||
continue; | ||
} | ||
|
||
if (ternary.TrueExpression is not AccessExpressionSyntax truePropertyAccess || | ||
!SyntaxIgnoringTriviaComparer.Instance.Equals(functionCall.Arguments[0].Expression, truePropertyAccess.BaseExpression)) | ||
{ | ||
continue; | ||
} | ||
|
||
if (!truePropertyAccess.AccessExpressionMatches(functionCall.Arguments[1].Expression)) | ||
{ | ||
continue; | ||
} | ||
|
||
var replacement = SyntaxFactory.CreateBinaryOperationSyntax( | ||
SyntaxFactory.CreateSafeAccess(truePropertyAccess.BaseExpression, functionCall.Arguments[1].Expression), | ||
TokenType.DoubleQuestion, | ||
ternary.FalseExpression); | ||
|
||
yield return CreateFixableDiagnosticForSpan( | ||
diagnosticLevel, | ||
ternary.Span, | ||
new CodeFix( | ||
CoreResources.UseSafeAccessRule_CodeFix, | ||
isPreferred: true, | ||
CodeFixKind.QuickFix, | ||
new CodeReplacement(ternary.Span, replacement.ToString())), | ||
CoreResources.UseSafeAccessRule_MessageFormat); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters