Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New rule S4545: "DebuggerDisplayAttribute" strings should reference e…
…xisting members (#6728)
- Loading branch information
1 parent
ea89757
commit 6aa5454
Showing
21 changed files
with
938 additions
and
2 deletions.
There are no files selected for viewing
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,23 @@ | ||
<p>The <code>DebuggerDisplayAttribute</code> is used to determine how an object is displayed in the debugger window.</p> | ||
<p>The <code>DebuggerDisplayAttribute</code> constructor takes a single argument: the string to be displayed in the value column for instances of the | ||
type. Any text within curly braces is evaluated as the name of a field, property, or method.</p> | ||
<p>Naming a non-existent field, property or method between curly braces will result in a CS0103 error in the debug window when debugging objects. | ||
Although there is no impact on the production code, providing a wrong value can lead to difficulties when debugging the application.</p> | ||
<p>This rule raises an issue when text specified between curly braces refers to members that don’t exist in the current context.</p> | ||
<h2>Noncompliant Code Example</h2> | ||
<pre> | ||
[DebuggerDisplay("Name: {Name}")] // Noncompliant - Name doesn't exist in this context | ||
public class Person | ||
{ | ||
public string FullName { get; private set; } | ||
} | ||
</pre> | ||
<h2>Compliant Solution</h2> | ||
<pre> | ||
[DebuggerDisplay("Name: {FullName}")] | ||
public class Person | ||
{ | ||
public string FullName { get; private set; } | ||
} | ||
</pre> | ||
|
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,15 @@ | ||
{ | ||
"title": "\"DebuggerDisplayAttribute\" strings should reference existing members", | ||
"type": "CODE_SMELL", | ||
"status": "ready", | ||
"remediation": { | ||
"func": "Constant\/Issue", | ||
"constantCost": "5min" | ||
}, | ||
"tags": [], | ||
"defaultSeverity": "Major", | ||
"ruleSpecification": "RSPEC-4545", | ||
"sqKey": "S4545", | ||
"scope": "All", | ||
"quickfix": "unknown" | ||
} |
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 |
---|---|---|
|
@@ -241,6 +241,7 @@ | |
"S4502", | ||
"S4507", | ||
"S4524", | ||
"S4545", | ||
"S4581", | ||
"S4583", | ||
"S4586", | ||
|
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,25 @@ | ||
<p>The <code>DebuggerDisplayAttribute</code> is used to determine how an object is displayed in the debugger window.</p> | ||
<p>The <code>DebuggerDisplayAttribute</code> constructor takes a single argument: the string to be displayed in the value column for instances of the | ||
type. Any text within curly braces is evaluated as the name of a field, property, or method.</p> | ||
<p>Naming a non-existent field, property or method between curly braces will result in a BC30451 error in the debug window when debugging objects. | ||
Although there is no impact on the production code, providing a wrong value can lead to difficulties when debugging the application.</p> | ||
<p>This rule raises an issue when text specified between curly braces refers to members that don’t exist in the current context.</p> | ||
<h2>Noncompliant Code Example</h2> | ||
<pre> | ||
<DebuggerDisplay("Name: {Name}")> ' Noncompliant - Name doesn't exist in this context | ||
Public Class Person | ||
|
||
Public Property FullName As String | ||
|
||
End Class | ||
</pre> | ||
<h2>Compliant Solution</h2> | ||
<pre> | ||
<DebuggerDisplay("Name: {FullName}")> | ||
Public Class Person | ||
|
||
Public Property FullName As String | ||
|
||
End Class | ||
</pre> | ||
|
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,15 @@ | ||
{ | ||
"title": "\"DebuggerDisplayAttribute\" strings should reference existing members", | ||
"type": "CODE_SMELL", | ||
"status": "ready", | ||
"remediation": { | ||
"func": "Constant\/Issue", | ||
"constantCost": "5min" | ||
}, | ||
"tags": [], | ||
"defaultSeverity": "Major", | ||
"ruleSpecification": "RSPEC-4545", | ||
"sqKey": "S4545", | ||
"scope": "All", | ||
"quickfix": "unknown" | ||
} |
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 |
---|---|---|
|
@@ -108,6 +108,7 @@ | |
"S4423", | ||
"S4428", | ||
"S4507", | ||
"S4545", | ||
"S4581", | ||
"S4583", | ||
"S4586", | ||
|
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
35 changes: 35 additions & 0 deletions
35
analyzers/src/SonarAnalyzer.CSharp/Rules/DebuggerDisplayUsesExistingMembers.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,35 @@ | ||
/* | ||
* SonarAnalyzer for .NET | ||
* Copyright (C) 2015-2023 SonarSource SA | ||
* mailto: contact AT sonarsource DOT com | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with this program; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
namespace SonarAnalyzer.Rules.CSharp; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public sealed class DebuggerDisplayUsesExistingMembers : DebuggerDisplayUsesExistingMembersBase<AttributeSyntax, SyntaxKind> | ||
{ | ||
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance; | ||
|
||
protected override SyntaxNode AttributeFormatString(AttributeSyntax attribute) => | ||
attribute.ArgumentList.Arguments.FirstOrDefault() is { Expression: LiteralExpressionSyntax { RawKind: (int)SyntaxKind.StringLiteralExpression } formatString } | ||
? formatString | ||
: null; | ||
|
||
protected override bool IsValidMemberName(string memberName) => | ||
SyntaxFacts.IsValidIdentifier(memberName); | ||
} |
26 changes: 26 additions & 0 deletions
26
analyzers/src/SonarAnalyzer.Common/Common/RegexConstants.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,26 @@ | ||
/* | ||
* SonarAnalyzer for .NET | ||
* Copyright (C) 2015-2023 SonarSource SA | ||
* mailto: contact AT sonarsource DOT com | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with this program; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
namespace SonarAnalyzer.Common; | ||
|
||
public static class RegexConstants | ||
{ | ||
public static TimeSpan DefaultTimeout => TimeSpan.FromMilliseconds(100); | ||
} |
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
101 changes: 101 additions & 0 deletions
101
analyzers/src/SonarAnalyzer.Common/Rules/DebuggerDisplayUsesExistingMembersBase.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,101 @@ | ||
/* | ||
* SonarAnalyzer for .NET | ||
* Copyright (C) 2015-2023 SonarSource SA | ||
* mailto: contact AT sonarsource DOT com | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with this program; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
using System.Text.RegularExpressions; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace SonarAnalyzer.Rules; | ||
|
||
public abstract class DebuggerDisplayUsesExistingMembersBase<TAttributeSyntax, TSyntaxKind> : SonarDiagnosticAnalyzer<TSyntaxKind> | ||
where TAttributeSyntax : SyntaxNode | ||
where TSyntaxKind : struct | ||
{ | ||
private const string DiagnosticId = "S4545"; | ||
|
||
private readonly Regex evaluatedExpressionRegex = new(@"\{(?<EvaluatedExpression>[^}]+)\}", RegexOptions.Multiline, RegexConstants.DefaultTimeout); | ||
|
||
protected abstract SyntaxNode AttributeFormatString(TAttributeSyntax attribute); | ||
protected abstract bool IsValidMemberName(string memberName); | ||
|
||
protected override string MessageFormat => "'{0}' doesn't exist in this context."; | ||
|
||
protected DebuggerDisplayUsesExistingMembersBase() : base(DiagnosticId) { } | ||
|
||
protected override void Initialize(SonarAnalysisContext context) => | ||
context.RegisterNodeAction(Language.GeneratedCodeRecognizer, | ||
c => | ||
{ | ||
var attribute = (TAttributeSyntax)c.Node; | ||
if (Language.Syntax.IsKnownAttributeType(attribute, KnownType.System_Diagnostics_DebuggerDisplayAttribute, c.SemanticModel) | ||
&& AttributeFormatString(attribute) is { } formatString | ||
&& Language.Syntax.StringValue(formatString, c.SemanticModel) is { } formatStringText | ||
&& FirstInvalidMemberName(c, formatStringText, attribute) is { } firstInvalidMember) | ||
{ | ||
c.ReportIssue(Diagnostic.Create(Rule, formatString.GetLocation(), firstInvalidMember)); | ||
} | ||
}, | ||
Language.SyntaxKind.Attribute); | ||
|
||
private string FirstInvalidMemberName(SonarSyntaxNodeReportingContext context, string formatString, TAttributeSyntax attributeSyntax) | ||
{ | ||
try | ||
{ | ||
return (attributeSyntax.Parent?.Parent is { } targetSyntax | ||
&& context.SemanticModel.GetDeclaredSymbol(targetSyntax) is { } targetSymbol | ||
&& TypeContainingReferencedMembers(targetSymbol) is { } typeSymbol) | ||
? FirstInvalidMemberName(typeSymbol) | ||
: null; | ||
} | ||
catch (RegexMatchTimeoutException) | ||
{ | ||
return null; | ||
} | ||
|
||
string FirstInvalidMemberName(ITypeSymbol typeSymbol) | ||
{ | ||
var allMembers = typeSymbol | ||
.GetSelfAndBaseTypes() | ||
.SelectMany(x => x.GetMembers()) | ||
.Select(x => x.Name) | ||
.ToHashSet(Language.NameComparer); | ||
|
||
foreach (Match match in evaluatedExpressionRegex.Matches(formatString)) | ||
{ | ||
if (match.Groups["EvaluatedExpression"] is { Success: true, Value: var evaluatedExpression } | ||
&& ExtractValidMemberName(evaluatedExpression) is { } memberName | ||
&& !allMembers.Contains(memberName)) | ||
{ | ||
return memberName; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
string ExtractValidMemberName(string evaluatedExpression) | ||
{ | ||
var sanitizedExpression = evaluatedExpression.Split(',')[0].Trim(); | ||
return IsValidMemberName(sanitizedExpression) ? sanitizedExpression : null; | ||
} | ||
|
||
static ITypeSymbol TypeContainingReferencedMembers(ISymbol symbol) => | ||
symbol is ITypeSymbol typeSymbol ? typeSymbol : symbol.ContainingType; | ||
} | ||
} |
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
35 changes: 35 additions & 0 deletions
35
analyzers/src/SonarAnalyzer.VisualBasic/Rules/DebuggerDisplayUsesExistingMembers.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,35 @@ | ||
/* | ||
* SonarAnalyzer for .NET | ||
* Copyright (C) 2015-2023 SonarSource SA | ||
* mailto: contact AT sonarsource DOT com | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public License | ||
* along with this program; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
namespace SonarAnalyzer.Rules.VisualBasic; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.VisualBasic)] | ||
public sealed class DebuggerDisplayUsesExistingMembers : DebuggerDisplayUsesExistingMembersBase<AttributeSyntax, SyntaxKind> | ||
{ | ||
protected override ILanguageFacade<SyntaxKind> Language => VisualBasicFacade.Instance; | ||
|
||
protected override SyntaxNode AttributeFormatString(AttributeSyntax attribute) => | ||
attribute.ArgumentList.Arguments.FirstOrDefault() is SimpleArgumentSyntax { Expression: LiteralExpressionSyntax { RawKind: (int)SyntaxKind.StringLiteralExpression } formatString } | ||
? formatString | ||
: null; | ||
|
||
protected override bool IsValidMemberName(string memberName) => | ||
SyntaxFacts.IsValidIdentifier(memberName); | ||
} |
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
Oops, something went wrong.