diff --git a/sonaranalyzer-dotnet/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S1135.json b/sonaranalyzer-dotnet/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S1135.json new file mode 100644 index 00000000000..93f3c774e31 --- /dev/null +++ b/sonaranalyzer-dotnet/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S1135.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIImages.vb", +"region": { +"startLine": 458, +"startColumn": 7, +"endLine": 458, +"endColumn": 11 +} +} +}, +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIModules.vb", +"region": { +"startLine": 781, +"startColumn": 40, +"endLine": 781, +"endColumn": 44 +} +} +} +] +} diff --git a/sonaranalyzer-dotnet/its/expected/Ember-MM/generic.EmberCore.MediaFileManager-{F6CACA89-E8E4-45D9-B942-97FBD4ADD106}-S1135.json b/sonaranalyzer-dotnet/its/expected/Ember-MM/generic.EmberCore.MediaFileManager-{F6CACA89-E8E4-45D9-B942-97FBD4ADD106}-S1135.json new file mode 100644 index 00000000000..74891856322 --- /dev/null +++ b/sonaranalyzer-dotnet/its/expected/Ember-MM/generic.EmberCore.MediaFileManager-{F6CACA89-E8E4-45D9-B942-97FBD4ADD106}-S1135.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.MediaFileManager\Module.MediaFileManagerModule.vb", +"region": { +"startLine": 231, +"startColumn": 38, +"endLine": 231, +"endColumn": 42 +} +} +}, +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.MediaFileManager\Module.MediaFileManagerModule.vb", +"region": { +"startLine": 239, +"startColumn": 38, +"endLine": 239, +"endColumn": 42 +} +} +} +] +} diff --git a/sonaranalyzer-dotnet/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S1135.json b/sonaranalyzer-dotnet/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S1135.json new file mode 100644 index 00000000000..11d480ee15a --- /dev/null +++ b/sonaranalyzer-dotnet/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S1135.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", +"region": { +"startLine": 1634, +"startColumn": 27, +"endLine": 1634, +"endColumn": 31 +} +} +}, +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\Module.NMT.vb", +"region": { +"startLine": 97, +"startColumn": 23, +"endLine": 97, +"endColumn": 27 +} +} +} +] +} diff --git a/sonaranalyzer-dotnet/its/expected/Ember-MM/scraper.EmberCore-{EF6A550E-DD76-4F4D-8250-8598140F828B}-S1135.json b/sonaranalyzer-dotnet/its/expected/Ember-MM/scraper.EmberCore-{EF6A550E-DD76-4F4D-8250-8598140F828B}-S1135.json new file mode 100644 index 00000000000..11a062e6551 --- /dev/null +++ b/sonaranalyzer-dotnet/its/expected/Ember-MM/scraper.EmberCore-{EF6A550E-DD76-4F4D-8250-8598140F828B}-S1135.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore\TVScraper\dlgTVImageSelect.vb", +"region": { +"startLine": 21, +"startColumn": 2, +"endLine": 21, +"endColumn": 6 +} +} +}, +{ +"id": "S1135", +"message": "Complete the task associated to this 'TODO' comment.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore\TVScraper\dlgTVImageSelect.vb", +"region": { +"startLine": 22, +"startColumn": 2, +"endLine": 22, +"endColumn": 6 +} +} +} +] +} diff --git a/sonaranalyzer-dotnet/rspec/vbnet/S1134_vb.net.html b/sonaranalyzer-dotnet/rspec/vbnet/S1134_vb.net.html new file mode 100644 index 00000000000..f9913bf53d4 --- /dev/null +++ b/sonaranalyzer-dotnet/rspec/vbnet/S1134_vb.net.html @@ -0,0 +1,14 @@ +

FIXME tags are commonly used to mark places where a bug is suspected, but which the developer wants to deal with later.

+

Sometimes the developer will not have the time or will simply forget to get back to that tag.

+

This rule is meant to track those tags and to ensure that they do not go unnoticed.

+

Noncompliant Code Example

+
+Function Divide(ByVal numerator As Integer, ByVal denominator As Integer) As Integer
+    Return numerator / denominator  ' FIXME denominator value might be  0
+End Function
+
+

See

+ + diff --git a/sonaranalyzer-dotnet/rspec/vbnet/S1134_vb.net.json b/sonaranalyzer-dotnet/rspec/vbnet/S1134_vb.net.json new file mode 100644 index 00000000000..1436329b833 --- /dev/null +++ b/sonaranalyzer-dotnet/rspec/vbnet/S1134_vb.net.json @@ -0,0 +1,20 @@ +{ + "title": "Track uses of \"FIXME\" tags", + "type": "CODE_SMELL", + "status": "ready", + "tags": [ + "cwe" + ], + "standards": [ + "CWE" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1134", + "sqKey": "S1134", + "scope": "All", + "securityStandards": { + "CWE": [ + 546 + ] + } +} diff --git a/sonaranalyzer-dotnet/rspec/vbnet/S1135_vb.net.html b/sonaranalyzer-dotnet/rspec/vbnet/S1135_vb.net.html new file mode 100644 index 00000000000..ceda945988a --- /dev/null +++ b/sonaranalyzer-dotnet/rspec/vbnet/S1135_vb.net.html @@ -0,0 +1,14 @@ +

TODO tags are commonly used to mark places where some more code is required, but which the developer wants to implement later.

+

Sometimes the developer will not have the time or will simply forget to get back to that tag.

+

This rule is meant to track those tags and to ensure that they do not go unnoticed.

+

Noncompliant Code Example

+
+Sub DoSomething()
+    ' TODO
+End Sub
+
+

See

+ + diff --git a/sonaranalyzer-dotnet/rspec/vbnet/S1135_vb.net.json b/sonaranalyzer-dotnet/rspec/vbnet/S1135_vb.net.json new file mode 100644 index 00000000000..fefb44a0cda --- /dev/null +++ b/sonaranalyzer-dotnet/rspec/vbnet/S1135_vb.net.json @@ -0,0 +1,20 @@ +{ + "title": "Track uses of \"TODO\" tags", + "type": "CODE_SMELL", + "status": "ready", + "tags": [ + "cwe" + ], + "standards": [ + "CWE" + ], + "defaultSeverity": "Info", + "ruleSpecification": "RSPEC-1135", + "sqKey": "S1135", + "scope": "All", + "securityStandards": { + "CWE": [ + 546 + ] + } +} diff --git a/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile.json b/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile.json index f04cfd4cc73..ebd9f51bc60 100644 --- a/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile.json +++ b/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile.json @@ -7,6 +7,8 @@ "S117", "S1048", "S1075", + "S1134", + "S1135", "S1186", "S1197", "S1313", diff --git a/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile_no_hotspot.json b/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile_no_hotspot.json index 6bfe798077b..bf4c7667720 100644 --- a/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile_no_hotspot.json +++ b/sonaranalyzer-dotnet/rspec/vbnet/Sonar_way_profile_no_hotspot.json @@ -7,6 +7,8 @@ "S117", "S1048", "S1075", + "S1134", + "S1135", "S1186", "S1197", "S1479", diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs index 00a32826b0e..050b75a3d74 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs @@ -346,5 +346,20 @@ public static bool IsLeftSideOfAssignment(this ExpressionSyntax expression) topParenthesizedExpression.Parent is AssignmentExpressionSyntax assignment && assignment.Left == topParenthesizedExpression; } + + public static bool IsComment(this SyntaxTrivia trivia) + { + switch (trivia.Kind()) + { + case SyntaxKind.SingleLineCommentTrivia: + case SyntaxKind.MultiLineCommentTrivia: + case SyntaxKind.SingleLineDocumentationCommentTrivia: + case SyntaxKind.MultiLineDocumentationCommentTrivia: + return true; + + default: + return false; + } + } } } diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Metrics/CSharpMetrics.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Metrics/CSharpMetrics.cs index df8035a8212..ffb57e380d7 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Metrics/CSharpMetrics.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Metrics/CSharpMetrics.cs @@ -74,20 +74,7 @@ protected override bool IsClass(SyntaxNode node) } } - protected override bool IsCommentTrivia(SyntaxTrivia trivia) - { - switch (trivia.Kind()) - { - case SyntaxKind.SingleLineCommentTrivia: - case SyntaxKind.MultiLineCommentTrivia: - case SyntaxKind.SingleLineDocumentationCommentTrivia: - case SyntaxKind.MultiLineDocumentationCommentTrivia: - return true; - - default: - return false; - } - } + protected override bool IsCommentTrivia(SyntaxTrivia trivia) => trivia.IsComment(); protected override bool IsDocumentationCommentTrivia(SyntaxTrivia trivia) { diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentFixme.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentKeyword.cs similarity index 53% rename from sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentFixme.cs rename to sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentKeyword.cs index 44f1a3e3b03..de3c9953b0e 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentFixme.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentKeyword.cs @@ -1,43 +1,47 @@ -/* - * SonarAnalyzer for .NET - * Copyright (C) 2015-2019 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.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using SonarAnalyzer.Common; -using SonarAnalyzer.Helpers; - -namespace SonarAnalyzer.Rules.CSharp -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - [Rule(DiagnosticId)] - public sealed class CommentFixme : CommentWordBase - { - protected override string Word => "FIXME"; - - internal const string DiagnosticId = "S1134"; - private const string MessageFormat = - "Take the required action to fix the issue indicated by this 'FIXME' comment."; - - private static readonly DiagnosticDescriptor rule = - DiagnosticDescriptorBuilder.GetDescriptor(DiagnosticId, MessageFormat, RspecStrings.ResourceManager); - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(rule); - } -} +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2019 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarAnalyzer.Common; +using SonarAnalyzer.Helpers; +using SonarAnalyzer.Helpers.CSharp; + +namespace SonarAnalyzer.Rules.CSharp +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + [Rule(FixMeDiagnosticId)] + [Rule(TodoDiagnosticId)] + public sealed class CommentKeyword : CommentKeywordBase + { + internal static readonly DiagnosticDescriptor TODO_Descriptor = + DiagnosticDescriptorBuilder.GetDescriptor(TodoDiagnosticId, TodoMessageFormat, RspecStrings.ResourceManager); + protected override DiagnosticDescriptor TodoDiagnostic { get; } = TODO_Descriptor; + + internal static readonly DiagnosticDescriptor FIXME_Descriptor = + DiagnosticDescriptorBuilder.GetDescriptor(FixMeDiagnosticId, FixMeMessageFormat, RspecStrings.ResourceManager); + protected override DiagnosticDescriptor FixMeDiagnostic { get; } = FIXME_Descriptor; + + protected override GeneratedCodeRecognizer GeneratedCodeRecognizer + => CSharpGeneratedCodeRecognizer.Instance; + + protected override bool IsComment(SyntaxTrivia trivia) => trivia.IsComment(); + } +} diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentTodo.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentTodo.cs deleted file mode 100644 index cc1e7a56073..00000000000 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentTodo.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SonarAnalyzer for .NET - * Copyright (C) 2015-2019 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.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using SonarAnalyzer.Common; -using SonarAnalyzer.Helpers; - -namespace SonarAnalyzer.Rules.CSharp -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - [Rule(DiagnosticId)] - public sealed class CommentTodo : CommentWordBase - { - protected override string Word => "TODO"; - - internal const string DiagnosticId = "S1135"; - private const string MessageFormat = - "Complete the task associated to this 'TODO' comment."; - - internal static readonly DiagnosticDescriptor rule = - DiagnosticDescriptorBuilder.GetDescriptor(DiagnosticId, MessageFormat, RspecStrings.ResourceManager); - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(rule); - } -} diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentWordBase.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentWordBase.cs deleted file mode 100644 index 98ed3f53628..00000000000 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/CommentWordBase.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SonarAnalyzer for .NET - * Copyright (C) 2015-2019 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.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; -using SonarAnalyzer.Helpers; - -namespace SonarAnalyzer.Rules.CSharp -{ - public abstract class CommentWordBase : SonarDiagnosticAnalyzer - { - protected abstract string Word { get; } - - protected sealed override void Initialize(SonarAnalysisContext context) - { - context.RegisterSyntaxTreeActionInNonGenerated( - c => - { - var comments = c.Tree.GetCompilationUnitRoot().DescendantTrivia() - .Where(trivia => IsComment(trivia)); - - foreach (var comment in comments) - { - var text = comment.ToString(); - - foreach (var i in AllCaseInsensitiveIndexesOf(text, Word).Where(i => IsWordAt(text, i, Word.Length))) - { - var startLocation = comment.SpanStart + i; - var location = Location.Create( - c.Tree, - TextSpan.FromBounds(startLocation, startLocation + Word.Length)); - - c.ReportDiagnosticWhenActive(Diagnostic.Create(SupportedDiagnostics[0], location)); - } - } - }); - } - - private static bool IsComment(SyntaxTrivia trivia) - { - return trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) || - trivia.IsKind(SyntaxKind.MultiLineCommentTrivia) || - trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) || - trivia.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia); - } - - private static IEnumerable AllCaseInsensitiveIndexesOf(string str, string value) - { - var index = 0; - while ((index = str.IndexOf(value, index, str.Length - index, System.StringComparison.OrdinalIgnoreCase)) != -1) - { - yield return index; - index += value.Length; - } - } - - private static bool IsWordAt(string str, int index, int count) - { - var leftBoundary = true; - if (index > 0) - { - leftBoundary = !char.IsLetterOrDigit(str[index - 1]); - } - - var rightBoundary = true; - var rightOffset = index + count; - if (rightOffset < str.Length) - { - rightBoundary = !char.IsLetterOrDigit(str[rightOffset]); - } - - return leftBoundary && rightBoundary; - } - } -} diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.Common/Rules/CommentKeywordBase.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.Common/Rules/CommentKeywordBase.cs new file mode 100644 index 00000000000..319d71a5b74 --- /dev/null +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.Common/Rules/CommentKeywordBase.cs @@ -0,0 +1,122 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2019 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; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using SonarAnalyzer.Helpers; + +namespace SonarAnalyzer.Rules +{ + public abstract class CommentKeywordBase : SonarDiagnosticAnalyzer + { + private const string TodoKeyword = "TODO"; + protected const string TodoDiagnosticId = "S1135"; + protected const string TodoMessageFormat = "Complete the task associated to this '" + TodoKeyword + "' comment."; + + private const string FixMeKeyword = "FIXME"; + protected const string FixMeDiagnosticId = "S1134"; + protected const string FixMeMessageFormat = "Take the required action to fix the issue indicated by this '" + FixMeKeyword + "' comment."; + + protected abstract DiagnosticDescriptor TodoDiagnostic { get; } + protected abstract DiagnosticDescriptor FixMeDiagnostic { get; } + protected abstract GeneratedCodeRecognizer GeneratedCodeRecognizer { get; } + + public sealed override ImmutableArray SupportedDiagnostics { get; } + + protected CommentKeywordBase() + { + SupportedDiagnostics = ImmutableArray.Create(TodoDiagnostic, FixMeDiagnostic); + } + + protected sealed override void Initialize(SonarAnalysisContext context) + { + context.RegisterSyntaxTreeActionInNonGenerated( + GeneratedCodeRecognizer, + c => + { + var comments = c.Tree.GetRoot().DescendantTrivia() + .Where(trivia => IsComment(trivia)); + + foreach (var comment in comments) + { + foreach (var location in GetKeywordLocations(c.Tree, comment, TodoKeyword)) + { + c.ReportDiagnosticWhenActive(Diagnostic.Create(TodoDiagnostic, location)); + } + + foreach (var location in GetKeywordLocations(c.Tree, comment, FixMeKeyword)) + { + c.ReportDiagnosticWhenActive(Diagnostic.Create(FixMeDiagnostic, location)); + } + } + }); + } + + protected abstract bool IsComment(SyntaxTrivia trivia); + + private static IEnumerable GetKeywordLocations(SyntaxTree tree, SyntaxTrivia comment, string word) + { + var text = comment.ToString(); + + return AllIndexesOf(text, word) + .Where(i => IsWordAt(text, i, word.Length)) + .Select( + i => + { + var startLocation = comment.SpanStart + i; + var location = Location.Create(tree, TextSpan.FromBounds(startLocation, startLocation + word.Length)); + + return location; + }); + } + + private static IEnumerable AllIndexesOf(string text, string value, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) + { + var index = 0; + while ((index = text.IndexOf(value, index, text.Length - index, comparisonType)) != -1) + { + yield return index; + index += value.Length; + } + } + + private static bool IsWordAt(string text, int index, int count) + { + var leftBoundary = true; + if (index > 0) + { + leftBoundary = !char.IsLetterOrDigit(text[index - 1]); + } + + var rightBoundary = true; + var rightOffset = index + count; + if (rightOffset < text.Length) + { + rightBoundary = !char.IsLetterOrDigit(text[rightOffset]); + } + + return leftBoundary && rightBoundary; + } + } +} diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134.html b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134_cs.html similarity index 99% rename from sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134.html rename to sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134_cs.html index 8e877afd34e..f772e2285ea 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134.html +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134_cs.html @@ -12,4 +12,4 @@

See

- + diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134_vb.html b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134_vb.html new file mode 100644 index 00000000000..f9913bf53d4 --- /dev/null +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1134_vb.html @@ -0,0 +1,14 @@ +

FIXME tags are commonly used to mark places where a bug is suspected, but which the developer wants to deal with later.

+

Sometimes the developer will not have the time or will simply forget to get back to that tag.

+

This rule is meant to track those tags and to ensure that they do not go unnoticed.

+

Noncompliant Code Example

+
+Function Divide(ByVal numerator As Integer, ByVal denominator As Integer) As Integer
+    Return numerator / denominator  ' FIXME denominator value might be  0
+End Function
+
+

See

+ + diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135.html b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135_cs.html similarity index 99% rename from sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135.html rename to sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135_cs.html index 6223daf3606..a80dd0ea821 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135.html +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135_cs.html @@ -12,4 +12,4 @@

See

- + diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135_vb.html b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135_vb.html new file mode 100644 index 00000000000..ceda945988a --- /dev/null +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.Utilities/Rules.Description/S1135_vb.html @@ -0,0 +1,14 @@ +

TODO tags are commonly used to mark places where some more code is required, but which the developer wants to implement later.

+

Sometimes the developer will not have the time or will simply forget to get back to that tag.

+

This rule is meant to track those tags and to ensure that they do not go unnoticed.

+

Noncompliant Code Example

+
+Sub DoSomething()
+    ' TODO
+End Sub
+
+

See

+ + diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs index 3b94da60d44..f8e51855c66 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs @@ -182,5 +182,19 @@ public static bool IsLeftSideOfAssignment(this ExpressionSyntax expression) topParenthesizedExpression.Parent is AssignmentStatementSyntax assignment && assignment.Left == topParenthesizedExpression; } + + public static bool IsComment(this SyntaxTrivia trivia) + { + switch (trivia.Kind()) + { + case SyntaxKind.CommentTrivia: + case SyntaxKind.DocumentationCommentExteriorTrivia: + case SyntaxKind.DocumentationCommentTrivia: + return true; + + default: + return false; + } + } } } diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Metrics/VisualBasicMetrics.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Metrics/VisualBasicMetrics.cs index a63c6756d85..a0d64ba251c 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Metrics/VisualBasicMetrics.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Metrics/VisualBasicMetrics.cs @@ -26,6 +26,7 @@ using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using SonarAnalyzer.Common; +using SonarAnalyzer.Helpers.VisualBasic; namespace SonarAnalyzer.Metrics.VisualBasic { @@ -105,19 +106,7 @@ protected override bool IsClass(SyntaxNode node) } } - protected override bool IsCommentTrivia(SyntaxTrivia trivia) - { - switch (trivia.Kind()) - { - case SyntaxKind.CommentTrivia: - case SyntaxKind.DocumentationCommentExteriorTrivia: - case SyntaxKind.DocumentationCommentTrivia: - return true; - - default: - return false; - } - } + protected override bool IsCommentTrivia(SyntaxTrivia trivia) => trivia.IsComment(); protected override bool IsDocumentationCommentTrivia(SyntaxTrivia trivia) { diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Rules/CommentKeyword.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Rules/CommentKeyword.cs new file mode 100644 index 00000000000..263000cff59 --- /dev/null +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.VisualBasic/Rules/CommentKeyword.cs @@ -0,0 +1,47 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2019 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarAnalyzer.Common; +using SonarAnalyzer.Helpers; +using SonarAnalyzer.Helpers.VisualBasic; + +namespace SonarAnalyzer.Rules.VisualBasic +{ + [DiagnosticAnalyzer(LanguageNames.VisualBasic)] + [Rule(FixMeDiagnosticId)] + [Rule(TodoDiagnosticId)] + public sealed class CommentKeyword : CommentKeywordBase + { + internal static readonly DiagnosticDescriptor TODO_Descriptor = + DiagnosticDescriptorBuilder.GetDescriptor(TodoDiagnosticId, TodoMessageFormat, RspecStrings.ResourceManager); + protected override DiagnosticDescriptor TodoDiagnostic { get; } = TODO_Descriptor; + + internal static readonly DiagnosticDescriptor FIXME_Descriptor = + DiagnosticDescriptorBuilder.GetDescriptor(FixMeDiagnosticId, FixMeMessageFormat, RspecStrings.ResourceManager); + protected override DiagnosticDescriptor FixMeDiagnostic { get; } = FIXME_Descriptor; + + protected override GeneratedCodeRecognizer GeneratedCodeRecognizer + => VisualBasicGeneratedCodeRecognizer.Instance; + + protected override bool IsComment(SyntaxTrivia trivia) => trivia.IsComment(); + } +} diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/PackagingTests/VbRuleTypeMapping.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/PackagingTests/VbRuleTypeMapping.cs index 6934891aaba..3ec16dcd53e 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/PackagingTests/VbRuleTypeMapping.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/PackagingTests/VbRuleTypeMapping.cs @@ -1131,8 +1131,8 @@ internal static class VbRuleTypeMapping //["1131"], //["1132"], //["1133"], - //["1134"], - //["1135"], + ["1134"] = "CODE_SMELL", + ["1135"] = "CODE_SMELL", //["1136"], //["1137"], //["1138"], diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentFixmeTest.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentFixmeTest.cs deleted file mode 100644 index 9632af3caad..00000000000 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentFixmeTest.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SonarAnalyzer for .NET - * Copyright (C) 2015-2019 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. - */ - -extern alias csharp; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using csharp::SonarAnalyzer.Rules.CSharp; - -namespace SonarAnalyzer.UnitTest.Rules -{ - [TestClass] - public class CommentFixmeTest - { - [TestMethod] - [TestCategory("Rule")] - public void CommentFixme() - { - Verifier.VerifyAnalyzer(@"TestCases\CommentFixme.cs", new CommentFixme()); - } - } -} diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentTodoTest.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentKeywordTest.cs similarity index 55% rename from sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentTodoTest.cs rename to sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentKeywordTest.cs index 59357216b71..160f8b68eca 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentTodoTest.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/CommentKeywordTest.cs @@ -19,19 +19,46 @@ */ extern alias csharp; +extern alias vbnet; using Microsoft.VisualStudio.TestTools.UnitTesting; -using csharp::SonarAnalyzer.Rules.CSharp; +using CSharp = csharp::SonarAnalyzer.Rules.CSharp; +using VisualBasic = vbnet::SonarAnalyzer.Rules.VisualBasic; namespace SonarAnalyzer.UnitTest.Rules { [TestClass] - public class CommentTodoTest + public class CommentKeywordTest { [TestMethod] [TestCategory("Rule")] - public void CommentTodo() + public void CommentTodo_CS() { - Verifier.VerifyAnalyzer(@"TestCases\CommentTodo.cs", new CommentTodo()); + Verifier.VerifyAnalyzer(@"TestCases\CommentTodo.cs", + new CSharp.CommentKeyword()); + } + + [TestMethod] + [TestCategory("Rule")] + public void CommentFixme_CS() + { + Verifier.VerifyAnalyzer(@"TestCases\CommentFixme.cs", + new CSharp.CommentKeyword()); + } + + [TestMethod] + [TestCategory("Rule")] + public void CommentTodo_VB() + { + Verifier.VerifyAnalyzer(@"TestCases\CommentTodo.vb", + new VisualBasic.CommentKeyword()); + } + + [TestMethod] + [TestCategory("Rule")] + public void CommentFixme_VB() + { + Verifier.VerifyAnalyzer(@"TestCases\CommentFixme.vb", + new VisualBasic.CommentKeyword()); } } } diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.cs index 29a826f8f22..a11d9998aac 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.cs @@ -31,4 +31,10 @@ */ namespace Tests.Diagnostics { + class FixMe + { + public FixMe() + { + } + } } diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.vb b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.vb new file mode 100644 index 00000000000..f387a1978ec --- /dev/null +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentFixme.vb @@ -0,0 +1,22 @@ +Imports System +Imports System.Collections.Generic +Imports System.Linq +Imports System.Text + +Namespace Tests.TestCases + ''' + ''' FIXME: Do something ' Noncompliant + ''' + Class Foo + Public Sub Test() ' FIXME +' ^^^^^ + + End Sub + End Class + + Class FixMe + Sub FixMe() + + End Sub + End Class +End Namespace 'fixme ' Noncompliant diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.cs index bec6c5244b8..50b5899f8ee 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.cs @@ -31,4 +31,10 @@ */ namespace Tests.Diagnostics { -} \ No newline at end of file + class Todo + { + public Todo() + { + } + } +} diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.vb b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.vb new file mode 100644 index 00000000000..9cbecea6e91 --- /dev/null +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/CommentTodo.vb @@ -0,0 +1,21 @@ +Imports System +Imports System.Collections.Generic +Imports System.Linq +Imports System.Text + +Namespace Tests.TestCases + ''' + ''' TODO: Do something ' Noncompliant + ''' + Class Foo + Public Sub Test() ' TODO ' Noncompliant +' ^^^^ + + End Sub + End Class + + Class Todo + Sub Todo() ' this is not a ctor + End Sub + End Class +End Namespace 'todo ' Noncompliant