Skip to content

Commit

Permalink
Refactor the logic of S4663
Browse files Browse the repository at this point in the history
  • Loading branch information
gregory-paidis-sonarsource committed Jan 26, 2023
1 parent c1cf2c4 commit beb705c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public sealed class CommentsShouldNotBeEmpty : CommentsShouldNotBeEmptyBase<Synt
{
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;

protected override bool IsValidTriviaType(SyntaxTrivia trivia)
=> Language.SyntaxKind.CommentTrivia.Contains(trivia.Kind());

protected override bool IsSimpleComment(SyntaxTrivia trivia) => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia);
protected override bool IsEndOfLine(SyntaxTrivia trivia) => trivia.IsKind(SyntaxKind.EndOfLineTrivia);
protected override bool IsWhitespace(SyntaxTrivia trivia) => trivia.IsKind(SyntaxKind.WhitespaceTrivia);

protected override string GetCommentText(SyntaxTrivia trivia)
=> trivia.Kind() switch
{
Expand All @@ -36,17 +43,10 @@ protected override string GetCommentText(SyntaxTrivia trivia)
SyntaxKind.MultiLineDocumentationCommentTrivia => GetMultiLineDocumentationText(trivia),
};

protected override bool IsValidTriviaType(SyntaxTrivia trivia)
=> Language.SyntaxKind.CommentTrivia.Contains(trivia.Kind());

// //
private static string GetSingleLineText(SyntaxTrivia trivia)
=> trivia.ToString().Trim().Substring(2);

// /* */
private static string GetMultiLineText(SyntaxTrivia trivia) =>
ParseMultiLine(trivia.ToString(), 2); // Length of "/*"

// ///
private static string GetSingleLineDocumentationText(SyntaxTrivia trivia)
{
Expand All @@ -64,6 +64,10 @@ private static string GetSingleLineDocumentationText(SyntaxTrivia trivia)
return stringBuilder.ToString();
}

// /* */
private static string GetMultiLineText(SyntaxTrivia trivia) =>
ParseMultiLine(trivia.ToString(), 2); // Length of "/*"

// /** */
private static string GetMultiLineDocumentationText(SyntaxTrivia trivia) =>
ParseMultiLine(trivia.ToFullString(), 3); // Length of "/**"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using Microsoft.CodeAnalysis.Text;

namespace SonarAnalyzer.Rules;

public abstract class CommentsShouldNotBeEmptyBase<TSyntaxKind> : SonarDiagnosticAnalyzer<TSyntaxKind>
where TSyntaxKind : struct
{
private const string DiagnosticId = "S4663";

protected abstract bool IsValidTriviaType(SyntaxTrivia trivia);
protected abstract string GetCommentText(SyntaxTrivia trivia);
protected abstract bool IsValidTriviaType(SyntaxTrivia trivia);

protected abstract bool IsSimpleComment(SyntaxTrivia trivia);
protected abstract bool IsEndOfLine(SyntaxTrivia trivia);
protected abstract bool IsWhitespace(SyntaxTrivia trivia);

protected override string MessageFormat => "Remove this empty comment";

Expand All @@ -44,12 +50,77 @@ public abstract class CommentsShouldNotBeEmptyBase<TSyntaxKind> : SonarDiagnosti

protected void CheckTrivia(SonarSyntaxTreeReportingContext context, IEnumerable<SyntaxTrivia> trivia)
{
foreach (var trivium in trivia.Where(ShouldReport))
foreach (var partition in Partition(trivia).Where(ShouldReport))
{
var start = partition.First().GetLocation().SourceSpan.Start;
var end = partition.Last().GetLocation().SourceSpan.End;

var location = Location.Create(context.Tree, TextSpan.FromBounds(start, end));
context.ReportIssue(Diagnostic.Create(Rule, location));
}
}

protected IEnumerable<IEnumerable<SyntaxTrivia>> Partition(IEnumerable<SyntaxTrivia> trivia)
{
var res = new List<List<SyntaxTrivia>>();

var current = new List<SyntaxTrivia>();
var endOfLineFound = false;

foreach (var trivium in trivia)
{
context.ReportIssue(Diagnostic.Create(Rule, trivium.GetLocation()));
if (IsWhitespace(trivium))
{
continue;
}

if (IsSimpleComment(trivium)) // put it on the current block of "//"
{
current.Add(trivium);
endOfLineFound = false;
continue;
}

// This is for the case, of two different comment types, for example:
// //
// ///
if (IsValidTriviaType(trivium)) // valid but not "//", because of the upper if
{
AddCurrent();
res.Add(new List<SyntaxTrivia> { trivium });
}
else if (IsEndOfLine(trivium))
{
// This is for the case, of an empty line in between, for example:
// //
//
// //
if (endOfLineFound)
{
AddCurrent();
}
else
{
endOfLineFound = true;
}
}
else
{
AddCurrent();
}
}

bool ShouldReport(SyntaxTrivia trivia)
=> IsValidTriviaType(trivia) && string.IsNullOrEmpty(GetCommentText(trivia));
res.Add(current);
return res;

void AddCurrent()
{
res.Add(current);
current = new();
endOfLineFound = false;
}
}

protected bool ShouldReport(IEnumerable<SyntaxTrivia> trivia) =>
trivia.Any() && trivia.All(x => GetCommentText(x) == string.Empty);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@ public sealed class CommentsShouldNotBeEmpty : CommentsShouldNotBeEmptyBase<Synt
{
protected override ILanguageFacade<SyntaxKind> Language => VisualBasicFacade.Instance;

protected override bool IsValidTriviaType(SyntaxTrivia trivia)
=> Language.SyntaxKind.CommentTrivia.Contains(trivia.Kind());

protected override bool IsSimpleComment(SyntaxTrivia trivia) => trivia.IsKind(SyntaxKind.CommentTrivia);
protected override bool IsEndOfLine(SyntaxTrivia trivia) => trivia.IsKind(SyntaxKind.EndOfLineTrivia);
protected override bool IsWhitespace(SyntaxTrivia trivia) => trivia.IsKind(SyntaxKind.WhitespaceTrivia);

protected override string GetCommentText(SyntaxTrivia trivia)
=> trivia.Kind() switch
{
SyntaxKind.CommentTrivia => GetText(trivia),
SyntaxKind.DocumentationCommentTrivia => GetDocumentationText(trivia),
};

protected override bool IsValidTriviaType(SyntaxTrivia trivia)
=> Language.SyntaxKind.CommentTrivia.Contains(trivia.Kind());

// '
private static string GetText(SyntaxTrivia trivia)
=> trivia.ToString().Trim().Substring(1);
Expand Down

0 comments on commit beb705c

Please sign in to comment.