From 9411546896acf943933ab7af1a9800bbdace6e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Wed, 28 Jun 2023 09:21:40 +0200 Subject: [PATCH 01/12] Empty rule implementation --- analyzers/rspec/cs/S6562.html | 39 ++++++++++++++++++ analyzers/rspec/cs/S6562.json | 18 ++++++++ analyzers/rspec/cs/Sonar_way_profile.json | 1 + analyzers/rspec/vbnet/S6562.html | 37 +++++++++++++++++ analyzers/rspec/vbnet/S6562.json | 18 ++++++++ analyzers/rspec/vbnet/Sonar_way_profile.json | 1 + .../Rules/AlwaysSetDateTimeKind.cs | 39 ++++++++++++++++++ .../Rules/AlwaysSetDateTimeKindBase.cs | 31 ++++++++++++++ .../Rules/AlwaysSetDateTimeKind.cs | 39 ++++++++++++++++++ .../PackagingTests/RuleTypeMappingCS.cs | 2 +- .../PackagingTests/RuleTypeMappingVB.cs | 2 +- .../Rules/AlwaysSetDateTimeKindTest.cs | 41 +++++++++++++++++++ .../TestCases/AlwaysSetDateTimeKind.cs | 5 +++ .../TestCases/AlwaysSetDateTimeKind.vb | 7 ++++ 14 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 analyzers/rspec/cs/S6562.html create mode 100644 analyzers/rspec/cs/S6562.json create mode 100644 analyzers/rspec/vbnet/S6562.html create mode 100644 analyzers/rspec/vbnet/S6562.json create mode 100644 analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs create mode 100644 analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs create mode 100644 analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb diff --git a/analyzers/rspec/cs/S6562.html b/analyzers/rspec/cs/S6562.html new file mode 100644 index 00000000000..f89c7980ee1 --- /dev/null +++ b/analyzers/rspec/cs/S6562.html @@ -0,0 +1,39 @@ +

Why is this an issue?

+

Not knowing the Kind of the DateTime object that an application is using can lead to misunderstandings when displaying or +comparing them. Explicitly setting the Kind property helps the application to stay consistent, and its maintainers understand what kind +of date is being managed. To achieve this, when instantiating a new DateTime object you should always use a constructor overload that +allows you to define the Kind property.

+

What is the potential impact?

+

Creating the DateTime object without specifying the property Kind will set it to the default value of +DateTimeKind.Unspecified. In this case, calling the method ToUniversalTime will assume that Kind is +DateTimeKind.Local and calling the method ToLocalTime will assume that it’s DateTimeKind.Utc. As a result, you +might have mismatched DateTime objects in your application.

+

How to fix it

+

To fix this issue use a constructor overload that allows specifying the DateTimeKind when creating the DateTime +object.

+

Code examples

+

Noncompliant code example

+
+void CreateNewTime()
+{
+    var birthDate = new DateTime(1994, 7, 5, 16, 23, 42);
+}
+
+

Compliant solution

+
+void CreateNewTime()
+{
+    var birthDate = new DateTime(1994, 7, 5, 16, 23, 42, DateTimeKind.Utc);
+}
+
+

Resources

+

Documentation

+ + diff --git a/analyzers/rspec/cs/S6562.json b/analyzers/rspec/cs/S6562.json new file mode 100644 index 00000000000..cb2b7ef08cf --- /dev/null +++ b/analyzers/rspec/cs/S6562.json @@ -0,0 +1,18 @@ +{ + "title": "Always set the \"DateTimeKind\" when creating new \"DateTime\" instances", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "localisation", + "pitfall" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-6562", + "sqKey": "S6562", + "scope": "All", + "quickfix": "infeasible" +} diff --git a/analyzers/rspec/cs/Sonar_way_profile.json b/analyzers/rspec/cs/Sonar_way_profile.json index 0c67ee39df0..930b863c257 100644 --- a/analyzers/rspec/cs/Sonar_way_profile.json +++ b/analyzers/rspec/cs/Sonar_way_profile.json @@ -276,6 +276,7 @@ "S6424", "S6444", "S6561", + "S6562", "S6575", "S6588", "S6602", diff --git a/analyzers/rspec/vbnet/S6562.html b/analyzers/rspec/vbnet/S6562.html new file mode 100644 index 00000000000..2ce11dc0ebe --- /dev/null +++ b/analyzers/rspec/vbnet/S6562.html @@ -0,0 +1,37 @@ +

Why is this an issue?

+

Not knowing the Kind of the DateTime object that an application is using can lead to misunderstandings when displaying or +comparing them. Explicitly setting the Kind property helps the application to stay consistent, and its maintainers understand what kind +of date is being managed. To achieve this, when instantiating a new DateTime object you should always use a constructor overload that +allows you to define the Kind property.

+

What is the potential impact?

+

Creating the DateTime object without specifying the property Kind will set it to the default value of +DateTimeKind.Unspecified. In this case, calling the method ToUniversalTime will assume that Kind is +DateTimeKind.Local and calling the method ToLocalTime will assume that it’s DateTimeKind.Utc. As a result, you +might have mismatched DateTime objects in your application.

+

How to fix it

+

To fix this issue use a constructor overload that allows specifying the DateTimeKind when creating the DateTime +object.

+

Code examples

+

Noncompliant code example

+
+Private Sub CreateNewTime()
+    Dim birthDate = New DateTime(1994, 7, 5, 16, 23, 42)
+End Sub
+
+

Compliant solution

+
+Private Sub CreateNewTime()
+    Dim birthDate = New DateTime(1994, 7, 5, 16, 23, 42, DateTimeKind.Utc)
+End Sub
+
+

Resources

+

Documentation

+ + diff --git a/analyzers/rspec/vbnet/S6562.json b/analyzers/rspec/vbnet/S6562.json new file mode 100644 index 00000000000..cb2b7ef08cf --- /dev/null +++ b/analyzers/rspec/vbnet/S6562.json @@ -0,0 +1,18 @@ +{ + "title": "Always set the \"DateTimeKind\" when creating new \"DateTime\" instances", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "localisation", + "pitfall" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-6562", + "sqKey": "S6562", + "scope": "All", + "quickfix": "infeasible" +} diff --git a/analyzers/rspec/vbnet/Sonar_way_profile.json b/analyzers/rspec/vbnet/Sonar_way_profile.json index f0cd891b6dc..989cc4e2a2d 100644 --- a/analyzers/rspec/vbnet/Sonar_way_profile.json +++ b/analyzers/rspec/vbnet/Sonar_way_profile.json @@ -137,6 +137,7 @@ "S6146", "S6444", "S6561", + "S6562", "S6575", "S6588", "S6602", diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs new file mode 100644 index 00000000000..b5fb64a6bc2 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs @@ -0,0 +1,39 @@ +/* + * 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 AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase +{ + + protected override ILanguageFacade Language => CSharpFacade.Instance; + + protected override void Initialize(SonarAnalysisContext context) => + context.RegisterNodeAction(c => + { + var node = c.Node; + if (true) + { + c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); + } + }, + SyntaxKind.InvocationExpression); +} diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs new file mode 100644 index 00000000000..8d2d03eef84 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs @@ -0,0 +1,31 @@ +/* + * 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; + +public abstract class AlwaysSetDateTimeKindBase : SonarDiagnosticAnalyzer + where TSyntaxKind : struct +{ + private const string DiagnosticId = "S6562"; + + protected override string MessageFormat => "FIXME"; + + protected AlwaysSetDateTimeKindBase() : base(DiagnosticId) { } +} diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs new file mode 100644 index 00000000000..91bdec756d6 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs @@ -0,0 +1,39 @@ +/* + * 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 AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase +{ + + protected override ILanguageFacade Language => VisualBasicFacade.Instance; + + protected override void Initialize(SonarAnalysisContext context) => + context.RegisterNodeAction(c => + { + var node = c.Node; + if (true) + { + c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); + } + }, + SyntaxKind.InvocationExpression); +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs index baf9c4e5197..55d3821c13d 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs @@ -6486,7 +6486,7 @@ internal static class RuleTypeMappingCS // ["S6559"], // ["S6560"], ["S6561"] = "CODE_SMELL", - // ["S6562"], + ["S6562"] = "CODE_SMELL", // ["S6563"], // ["S6564"], // ["S6565"], diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs index ff98faf13d7..7687765a04c 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs @@ -6486,7 +6486,7 @@ internal static class RuleTypeMappingVB // ["S6559"], // ["S6560"], ["S6561"] = "CODE_SMELL", - // ["S6562"], + ["S6562"] = "CODE_SMELL", // ["S6563"], // ["S6564"], // ["S6565"], diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs new file mode 100644 index 00000000000..73ac6186a11 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -0,0 +1,41 @@ +/* + * 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 CS = SonarAnalyzer.Rules.CSharp; +using VB = SonarAnalyzer.Rules.VisualBasic; + +namespace SonarAnalyzer.UnitTest.Rules; + +[TestClass] +public class AlwaysSetDateTimeKindTest +{ + private readonly VerifierBuilder builderCS = new VerifierBuilder(); + + [TestMethod] + public void AlwaysSetDateTimeKind_CS() => + builderCS.AddPaths("AlwaysSetDateTimeKind.cs").Verify(); + + private readonly VerifierBuilder builderVB = new VerifierBuilder(); // FIXME: Move this up + + [TestMethod] + public void AlwaysSetDateTimeKind_VB() => + builderVB.AddPaths("AlwaysSetDateTimeKind.vb").Verify(); +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs new file mode 100644 index 00000000000..a790d6d1f92 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs @@ -0,0 +1,5 @@ +using System; + +public class Program +{ +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb new file mode 100644 index 00000000000..f5b9e9276c6 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb @@ -0,0 +1,7 @@ +Public Class Program + + Public Sub Test() + + End Sub + +End Class From c0d650adae4e388d3c5716374c96f9c79d45b3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Thu, 29 Jun 2023 10:48:58 +0200 Subject: [PATCH 02/12] First babystep --- .../Rules/AlwaysSetDateTimeKind.cs | 10 ++++---- .../SonarAnalyzer.Common/Helpers/KnownType.cs | 1 + .../Rules/AlwaysSetDateTimeKindTest.cs | 1 - .../TestCases/AlwaysSetDateTimeKind.cs | 24 +++++++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs index b5fb64a6bc2..a0c3e816a9d 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs @@ -29,11 +29,13 @@ public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase context.RegisterNodeAction(c => { - var node = c.Node; - if (true) + if (c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol ctor + && ctor.IsInType(KnownType.System_DateTime) + && !ctor.Parameters.Any(x => x.IsType(KnownType.System_DateTimeKind))) { - c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); + c.ReportIssue(Diagnostic.Create(Rule, c.Node.GetLocation())); } }, - SyntaxKind.InvocationExpression); + SyntaxKind.ObjectCreationExpression, + SyntaxKindEx.ImplicitObjectCreationExpression); } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs index b6f15ba0a60..8dd64005d06 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs @@ -267,6 +267,7 @@ public sealed partial class KnownType public static readonly KnownType System_Data_SqlServerCe_SqlCeDataAdapter = new("System.Data.SqlServerCe.SqlCeDataAdapter"); public static readonly KnownType System_DateOnly = new("System.DateOnly"); public static readonly KnownType System_DateTime = new("System.DateTime"); + public static readonly KnownType System_DateTimeKind = new("System.DateTimeKind"); public static readonly KnownType System_DateTimeOffset = new("System.DateTimeOffset"); public static readonly KnownType System_Decimal = new("System.Decimal"); public static readonly KnownType System_Delegate = new("System.Delegate"); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs index 73ac6186a11..590b44b4eec 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - using CS = SonarAnalyzer.Rules.CSharp; using VB = SonarAnalyzer.Rules.VisualBasic; diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs index a790d6d1f92..c8ac41e4211 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs @@ -1,5 +1,29 @@ using System; +using System.Globalization; public class Program { + public void Noncompliant() + { + var dt = new DateTime(); // Noncompliant + dt = new DateTime(1623); // Noncompliant + dt = new DateTime(1994, 07, 05); // Noncompliant + dt = new DateTime(1994, 07, 05, new GregorianCalendar()); // Noncompliant + dt = new DateTime(1994, 07, 05, 16, 23, 00); // Noncompliant + dt = new DateTime(1994, 07, 05, 16, 23, 00, new GregorianCalendar()); // Noncompliant + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42); // Noncompliant + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar()); // Noncompliant + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, 42); // Noncompliant + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar()); // Noncompliant + } + + public void Compliant() + { + var dt = new DateTime(1623, DateTimeKind.Unspecified); + dt = new DateTime(1994, 07, 05, 16, 23, 00, DateTimeKind.Local); + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar(), DateTimeKind.Unspecified); + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Utc); + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar(), DateTimeKind.Unspecified); + dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Unspecified); + } } From f07d3e8a3701784d9ed1350e322bb4e7eebc0775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Fri, 30 Jun 2023 05:12:11 +0200 Subject: [PATCH 03/12] Add visual basic implementation --- .../Rules/AlwaysSetDateTimeKind.cs | 14 ----------- .../Rules/AlwaysSetDateTimeKindBase.cs | 16 +++++++++++- .../Rules/AlwaysSetDateTimeKind.cs | 12 --------- .../Rules/AlwaysSetDateTimeKindTest.cs | 20 ++++++++++++--- .../TestCases/AlwaysSetDateTimeKind.cs | 3 +-- .../TestCases/AlwaysSetDateTimeKind.vb | 25 +++++++++++++++++-- 6 files changed, 56 insertions(+), 34 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs index a0c3e816a9d..5fff3b8acff 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs @@ -23,19 +23,5 @@ namespace SonarAnalyzer.Rules.CSharp; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase { - protected override ILanguageFacade Language => CSharpFacade.Instance; - - protected override void Initialize(SonarAnalysisContext context) => - context.RegisterNodeAction(c => - { - if (c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol ctor - && ctor.IsInType(KnownType.System_DateTime) - && !ctor.Parameters.Any(x => x.IsType(KnownType.System_DateTimeKind))) - { - c.ReportIssue(Diagnostic.Create(Rule, c.Node.GetLocation())); - } - }, - SyntaxKind.ObjectCreationExpression, - SyntaxKindEx.ImplicitObjectCreationExpression); } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs index 8d2d03eef84..45051a54126 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using Microsoft.CodeAnalysis.CSharp; + namespace SonarAnalyzer.Rules; public abstract class AlwaysSetDateTimeKindBase : SonarDiagnosticAnalyzer @@ -25,7 +27,19 @@ public abstract class AlwaysSetDateTimeKindBase : SonarDiagnosticAn { private const string DiagnosticId = "S6562"; - protected override string MessageFormat => "FIXME"; + protected override string MessageFormat => "Provide the \"DateTimeKind\" when creating this object."; protected AlwaysSetDateTimeKindBase() : base(DiagnosticId) { } + + protected override void Initialize(SonarAnalysisContext context) => + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => + { + if (c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol ctor + && ctor.IsInType(KnownType.System_DateTime) + && !ctor.Parameters.Any(x => x.IsType(KnownType.System_DateTimeKind))) + { + c.ReportIssue(Diagnostic.Create(Rule, c.Node.GetLocation())); + } + }, + Language.SyntaxKind.ObjectCreationExpressions); } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs index 91bdec756d6..caab4fffe2f 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs @@ -23,17 +23,5 @@ namespace SonarAnalyzer.Rules.VisualBasic; [DiagnosticAnalyzer(LanguageNames.VisualBasic)] public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase { - protected override ILanguageFacade Language => VisualBasicFacade.Instance; - - protected override void Initialize(SonarAnalysisContext context) => - context.RegisterNodeAction(c => - { - var node = c.Node; - if (true) - { - c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); - } - }, - SyntaxKind.InvocationExpression); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs index 590b44b4eec..4ac8fd1433d 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -32,9 +32,23 @@ public class AlwaysSetDateTimeKindTest public void AlwaysSetDateTimeKind_CS() => builderCS.AddPaths("AlwaysSetDateTimeKind.cs").Verify(); - private readonly VerifierBuilder builderVB = new VerifierBuilder(); // FIXME: Move this up - [TestMethod] public void AlwaysSetDateTimeKind_VB() => - builderVB.AddPaths("AlwaysSetDateTimeKind.vb").Verify(); + new VerifierBuilder().AddPaths("AlwaysSetDateTimeKind.vb").Verify(); + +#if NET + + [TestMethod] + public void AlwaysSetDateTimeKind_CSharp9() => + builderCS.AddSnippet( + """ + using System; + + DateTime dateTime = new(1994, 07, 05, 16, 23, 00, 42, 42); // Noncompliant + """) + .WithTopLevelStatements() + .Verify(); + +#endif + } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs index c8ac41e4211..2a0d04a89d7 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs @@ -5,7 +5,7 @@ public class Program { public void Noncompliant() { - var dt = new DateTime(); // Noncompliant + var dt = new DateTime(); // Noncompliant {{Provide the "DateTimeKind" when creating this object.}} dt = new DateTime(1623); // Noncompliant dt = new DateTime(1994, 07, 05); // Noncompliant dt = new DateTime(1994, 07, 05, new GregorianCalendar()); // Noncompliant @@ -13,7 +13,6 @@ public void Noncompliant() dt = new DateTime(1994, 07, 05, 16, 23, 00, new GregorianCalendar()); // Noncompliant dt = new DateTime(1994, 07, 05, 16, 23, 00, 42); // Noncompliant dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar()); // Noncompliant - dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, 42); // Noncompliant dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar()); // Noncompliant } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb index f5b9e9276c6..9d20fc091ea 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb @@ -1,7 +1,28 @@ -Public Class Program +Imports System.Globalization - Public Sub Test() +Public Class Program + Public Sub Noncompliant() + Dim dt = New DateTime() ' Noncompliant + dt = New Date() ' Noncompliant + dt = New dATEtIME() ' Noncompliant + dt = New DateTime(1623) ' Noncompliant + dt = New DateTime(1994, 7, 5) ' Noncompliant + dt = New DateTime(1994, 7, 5, New GregorianCalendar()) ' Noncompliant + dt = New DateTime(1994, 7, 5, 16, 23, 0) ' Noncompliant + dt = New DateTime(1994, 7, 5, 16, 23, 0, New GregorianCalendar()) ' Noncompliant + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42) ' Noncompliant + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar()) ' Noncompliant + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar()) ' Noncompliant + End Sub + + Public Sub Compiant() + Dim dt = New DateTime(1623, DateTimeKind.Unspecified) + dt = New DateTime(1994, 7, 5, 16, 23, 0, DateTimeKind.Local) + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar(), DateTimeKind.Unspecified) + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, DateTimeKind.Utc) + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar(), DateTimeKind.Unspecified) + dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, DateTimeKind.Unspecified) End Sub End Class From 8f8c056e77fe42c2a44f7c4a9bcde695e65f3b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Fri, 30 Jun 2023 05:20:03 +0200 Subject: [PATCH 04/12] Removed unused using --- .../src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs index 45051a54126..2b7ca6085c4 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs @@ -18,8 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using Microsoft.CodeAnalysis.CSharp; - namespace SonarAnalyzer.Rules; public abstract class AlwaysSetDateTimeKindBase : SonarDiagnosticAnalyzer From c40dafd7fecd3e0d21a5773831f8b5ede7c9110d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Mon, 3 Jul 2023 08:45:19 +0200 Subject: [PATCH 05/12] Improve performance by checking the type name first --- .../Facade/CSharpSyntaxFacade.cs | 3 ++ .../Helpers/CSharpSyntaxHelper.cs | 29 +++------------- .../Rules/AlwaysSetDateTimeKind.cs | 17 ++++++++++ .../Facade/SyntaxFacade.cs | 1 + .../Rules/AlwaysSetDateTimeKindBase.cs | 16 ++++++--- .../Facade/VisualBasicSyntaxFacade.cs | 3 ++ .../Helpers/VisualBasicSyntaxHelper.cs | 33 +++++++------------ .../Rules/AlwaysSetDateTimeKind.cs | 4 +++ .../TestCases/AlwaysSetDateTimeKind.cs | 16 +++++++++ .../TestCases/AlwaysSetDateTimeKind.vb | 17 +++++++++- 10 files changed, 89 insertions(+), 50 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Facade/CSharpSyntaxFacade.cs b/analyzers/src/SonarAnalyzer.CSharp/Facade/CSharpSyntaxFacade.cs index 0962d61c0f0..054c0d4d0fe 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Facade/CSharpSyntaxFacade.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Facade/CSharpSyntaxFacade.cs @@ -90,6 +90,9 @@ public override IEnumerable EnumMembers(SyntaxNode @enum) => public override SyntaxToken? InvocationIdentifier(SyntaxNode invocation) => invocation == null ? null : Cast(invocation).GetMethodCallIdentifier(); + public override SyntaxToken? ObjectCreationTypeIdentifier(SyntaxNode objectCreation) => + objectCreation == null ? null : Cast(objectCreation).GetObjectCreationTypeIdentifier(); + public override ImmutableArray LocalDeclarationIdentifiers(SyntaxNode node) => Cast(node).Declaration.Variables.Select(x => x.Identifier).ToImmutableArray(); diff --git a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs index a4f923a9608..e4ac6d00090 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs @@ -185,31 +185,11 @@ public static bool ContainsMethodInvocation(this BaseMethodDeclarationSyntax met } } - public static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) - { - if (invocation == null) - { - return null; - } - var expression = invocation.Expression; - switch (expression.Kind()) - { - case SyntaxKind.IdentifierName: - // method() - return ((IdentifierNameSyntax)expression).Identifier; + public static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) => + invocation == null ? null : GetIdentifier(invocation.Expression); - case SyntaxKind.SimpleMemberAccessExpression: - // foo.method() - return ((MemberAccessExpressionSyntax)expression).Name.Identifier; - - case SyntaxKind.MemberBindingExpression: - // foo?.method() - return ((MemberBindingExpressionSyntax)expression).Name.Identifier; - - default: - return null; - } - } + public static SyntaxToken? GetObjectCreationTypeIdentifier(this ObjectCreationExpressionSyntax objectCreation) => + objectCreation == null ? null : GetIdentifier(objectCreation.Type); public static bool IsMethodInvocation(this InvocationExpressionSyntax invocation, KnownType type, string methodName, SemanticModel semanticModel) => invocation.Expression.NameIs(methodName) && @@ -250,6 +230,7 @@ public static bool HasBodyOrExpressionBody(this AccessorDeclarationSyntax node) DelegateDeclarationSyntax { Identifier: var identifier } => identifier, DestructorDeclarationSyntax { Identifier: var identifier } => identifier, EnumMemberDeclarationSyntax { Identifier: var identifier } => identifier, + IdentifierNameSyntax { Identifier: var identifier } => identifier, IndexerDeclarationSyntax { ThisKeyword: var thisKeyword } => thisKeyword, InvocationExpressionSyntax { diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs index 5fff3b8acff..2f722d59556 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs @@ -24,4 +24,21 @@ namespace SonarAnalyzer.Rules.CSharp; public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase { protected override ILanguageFacade Language => CSharpFacade.Instance; + + protected override SyntaxKind ObjectCreationExpression => SyntaxKind.ObjectCreationExpression; + + protected override string[] ValidNames { get; } = new[] { "DateTime" }; + + protected override void Initialize(SonarAnalysisContext context) + { + base.Initialize(context); + context.RegisterNodeAction(c => + { + if (IsDateTimeConstructorWithoutKindParameter(c.Node, c.SemanticModel)) + { + c.ReportIssue(Diagnostic.Create(Rule, c.Node.GetLocation())); + } + }, + SyntaxKindEx.ImplicitObjectCreationExpression); + } } diff --git a/analyzers/src/SonarAnalyzer.Common/Facade/SyntaxFacade.cs b/analyzers/src/SonarAnalyzer.Common/Facade/SyntaxFacade.cs index ec7c64a5a59..6982a8396de 100644 --- a/analyzers/src/SonarAnalyzer.Common/Facade/SyntaxFacade.cs +++ b/analyzers/src/SonarAnalyzer.Common/Facade/SyntaxFacade.cs @@ -43,6 +43,7 @@ public abstract class SyntaxFacade public abstract SyntaxNode CastExpression(SyntaxNode cast); public abstract IEnumerable EnumMembers(SyntaxNode @enum); public abstract SyntaxToken? InvocationIdentifier(SyntaxNode invocation); + public abstract SyntaxToken? ObjectCreationTypeIdentifier(SyntaxNode objectCreation); public abstract ImmutableArray LocalDeclarationIdentifiers(SyntaxNode node); public abstract ImmutableArray FieldDeclarationIdentifiers(SyntaxNode node); public abstract TSyntaxKind[] ModifierKinds(SyntaxNode node); diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs index 2b7ca6085c4..cc3a548aa5c 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs @@ -25,6 +25,9 @@ public abstract class AlwaysSetDateTimeKindBase : SonarDiagnosticAn { private const string DiagnosticId = "S6562"; + protected abstract TSyntaxKind ObjectCreationExpression { get; } + protected abstract string[] ValidNames { get; } + protected override string MessageFormat => "Provide the \"DateTimeKind\" when creating this object."; protected AlwaysSetDateTimeKindBase() : base(DiagnosticId) { } @@ -32,12 +35,17 @@ protected AlwaysSetDateTimeKindBase() : base(DiagnosticId) { } protected override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { - if (c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol ctor - && ctor.IsInType(KnownType.System_DateTime) - && !ctor.Parameters.Any(x => x.IsType(KnownType.System_DateTimeKind))) + if (Language.Syntax.ObjectCreationTypeIdentifier(c.Node) is { } identifier + && ValidNames.Any(x => x.Equals(identifier.ValueText, Language.NameComparison)) + && IsDateTimeConstructorWithoutKindParameter(c.Node, c.SemanticModel)) { c.ReportIssue(Diagnostic.Create(Rule, c.Node.GetLocation())); } }, - Language.SyntaxKind.ObjectCreationExpressions); + ObjectCreationExpression); + + protected static bool IsDateTimeConstructorWithoutKindParameter(SyntaxNode objectCreation, SemanticModel semanticModel) => + semanticModel.GetSymbolInfo(objectCreation).Symbol is IMethodSymbol ctor + && ctor.IsInType(KnownType.System_DateTime) + && !ctor.Parameters.Any(x => x.IsType(KnownType.System_DateTimeKind)); } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Facade/VisualBasicSyntaxFacade.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Facade/VisualBasicSyntaxFacade.cs index 6167fc17bf9..05ce0616c44 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Facade/VisualBasicSyntaxFacade.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Facade/VisualBasicSyntaxFacade.cs @@ -88,6 +88,9 @@ public override IEnumerable EnumMembers(SyntaxNode @enum) => public override SyntaxToken? InvocationIdentifier(SyntaxNode invocation) => invocation == null ? null : Cast(invocation).GetMethodCallIdentifier(); + public override SyntaxToken? ObjectCreationTypeIdentifier(SyntaxNode objectCreation) => + objectCreation == null ? null : Cast(objectCreation).GetObjectCreationTypeIdentifier(); + public override ImmutableArray LocalDeclarationIdentifiers(SyntaxNode node) => Cast(node).Declarators.SelectMany(d => d.Names.Select(n => n.Identifier)).ToImmutableArray(); diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs index bd45da1a367..90e4310ae03 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs @@ -104,32 +104,23 @@ public static bool IsAnyKind(this SyntaxTrivia syntaxTrivia, params SyntaxKind[] public static bool AnyOfKind(this IEnumerable nodes, SyntaxKind kind) => nodes.Any(n => n.RawKind == (int)kind); - public static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) - { - if (invocation == null || - invocation.Expression == null) - { - return null; - } + public static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) => + invocation == null + || invocation.Expression == null + ? null + : GetIdentifier(invocation.Expression); + + public static SyntaxToken? GetObjectCreationTypeIdentifier(this ObjectCreationExpressionSyntax objectCreation) => + objectCreation == null + || objectCreation.Type == null + ? null + : GetIdentifier(objectCreation.Type); - var expressionType = invocation.Expression.Kind(); - // in vb.net when using the null - conditional operator (e.g.handle?.IsClosed), the parser - // will generate a SimpleMemberAccessExpression and not a MemberBindingExpressionSyntax like for C# - switch (expressionType) - { - case SyntaxKind.IdentifierName: - return ((IdentifierNameSyntax)invocation.Expression).Identifier; - case SyntaxKind.SimpleMemberAccessExpression: - return ((MemberAccessExpressionSyntax)invocation.Expression).Name.Identifier; - default: - return null; - } - } public static bool IsMethodInvocation(this InvocationExpressionSyntax expression, KnownType type, string methodName, SemanticModel semanticModel) => semanticModel.GetSymbolInfo(expression).Symbol is IMethodSymbol methodSymbol && methodSymbol.IsInType(type) && // vbnet is case insensitive - methodName.Equals(methodSymbol.Name, System.StringComparison.InvariantCultureIgnoreCase); + methodName.Equals(methodSymbol.Name, StringComparison.InvariantCultureIgnoreCase); public static bool IsOnBase(this ExpressionSyntax expression) => IsOn(expression, SyntaxKind.MyBaseExpression); diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs index caab4fffe2f..05c2738e3e3 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs @@ -24,4 +24,8 @@ namespace SonarAnalyzer.Rules.VisualBasic; public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase { protected override ILanguageFacade Language => VisualBasicFacade.Instance; + + protected override SyntaxKind ObjectCreationExpression => SyntaxKind.ObjectCreationExpression; + + protected override string[] ValidNames { get; } = new[] { "DateTime", "Date" }; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs index 2a0d04a89d7..4d1e2c236a2 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using MyAlias = System.DateTime; public class Program { @@ -14,6 +15,8 @@ public void Noncompliant() dt = new DateTime(1994, 07, 05, 16, 23, 00, 42); // Noncompliant dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar()); // Noncompliant dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar()); // Noncompliant + dt = new MyAlias(); // FN + dt = new System.DateTime(); // Noncompliant } public void Compliant() @@ -26,3 +29,16 @@ public void Compliant() dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Unspecified); } } + +public class FakeDateTime +{ + private class DateTime + { + + } + + private void Compliant() + { + var dt = new DateTime(); + } +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb index 9d20fc091ea..2dacf517bba 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb @@ -1,9 +1,10 @@ Imports System.Globalization +Imports MyAlias = System.DateTime Public Class Program Public Sub Noncompliant() - Dim dt = New DateTime() ' Noncompliant + Dim dt = New DateTime() ' Noncompliant {{Provide the "DateTimeKind" when creating this object.}} dt = New Date() ' Noncompliant dt = New dATEtIME() ' Noncompliant dt = New DateTime(1623) ' Noncompliant @@ -14,6 +15,8 @@ Public Class Program dt = New DateTime(1994, 7, 5, 16, 23, 0, 42) ' Noncompliant dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar()) ' Noncompliant dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar()) ' Noncompliant + dt = New MyAlias() ' FN + dt = New System.DateTime() ' Noncompliant End Sub Public Sub Compiant() @@ -26,3 +29,15 @@ Public Class Program End Sub End Class + +Class FakeDateTime + + Private Class DateTime + + End Class + + Private Sub Compliant() + Dim dt = New DateTime() + End Sub + +End Class From 9485f2d0be4be8537d0dcba0e38dc408363adfb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Mon, 3 Jul 2023 09:00:30 +0200 Subject: [PATCH 06/12] Update expected IT findings --- ...5E-C6AE-4D2D-A9DD-B6EFD19A4279}-S6562.json | 43 ++++++ .../akka.net/Akka--netstandard2.0-S6562.json | 17 +++ ...DistributedData--netstandard2.0-S6562.json | 17 +++ ...stRunner.Shared--netstandard2.0-S6562.json | 134 ++++++++++++++++++ ...kka.Persistence--netstandard2.0-S6562.json | 30 ++++ ...sistence.Sqlite--netstandard2.0-S6562.json | 17 +++ ...Persistence.TCK--netstandard2.0-S6562.json | 17 +++ 7 files changed, 275 insertions(+) create mode 100644 analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S6562.json create mode 100644 analyzers/its/expected/akka.net/Akka--netstandard2.0-S6562.json create mode 100644 analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S6562.json create mode 100644 analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S6562.json create mode 100644 analyzers/its/expected/akka.net/Akka.Persistence--netstandard2.0-S6562.json create mode 100644 analyzers/its/expected/akka.net/Akka.Persistence.Sqlite--netstandard2.0-S6562.json create mode 100644 analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S6562.json diff --git a/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S6562.json b/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S6562.json new file mode 100644 index 00000000000..a584d74ec06 --- /dev/null +++ b/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S6562.json @@ -0,0 +1,43 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPICommon.vb", +"region": { +"startLine": 711, +"startColumn": 34, +"endLine": 711, +"endColumn": 70 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPICommon.vb", +"region": { +"startLine": 716, +"startColumn": 34, +"endLine": 716, +"endColumn": 70 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIScanner.vb", +"region": { +"startLine": 32, +"startColumn": 30, +"endLine": 32, +"endColumn": 42 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka--netstandard2.0-S6562.json b/analyzers/its/expected/akka.net/Akka--netstandard2.0-S6562.json new file mode 100644 index 00000000000..fdac61aa174 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka--netstandard2.0-S6562.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka\Util\Extensions\DateTimeExtensions.cs", +"region": { +"startLine": 22, +"startColumn": 38, +"endLine": 22, +"endColumn": 62 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S6562.json b/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S6562.json new file mode 100644 index 00000000000..55e93c4fc94 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S6562.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\contrib\cluster\Akka.DistributedData\Serialization\ReplicatorMessageSerializer.cs", +"region": { +"startLine": 578, +"startColumn": 67, +"endLine": 578, +"endColumn": 99 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S6562.json b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S6562.json new file mode 100644 index 00000000000..81e8c88ecd5 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S6562.json @@ -0,0 +1,134 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Persistence\TimelineItemFactory.cs", +"region": { +"startLine": 38, +"startColumn": 73, +"endLine": 38, +"endColumn": 101 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Persistence\TimelineItemFactory.cs", +"region": { +"startLine": 48, +"startColumn": 84, +"endLine": 48, +"endColumn": 112 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Persistence\VisualizerRuntimeTemplate.Tree.cs", +"region": { +"startLine": 100, +"startColumn": 38, +"endLine": 100, +"endColumn": 71 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Persistence\VisualizerRuntimeTemplate.Tree.cs", +"region": { +"startLine": 101, +"startColumn": 38, +"endLine": 101, +"endColumn": 70 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunTree.cs", +"region": { +"startLine": 61, +"startColumn": 45, +"endLine": 61, +"endColumn": 72 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunTree.cs", +"region": { +"startLine": 61, +"startColumn": 94, +"endLine": 61, +"endColumn": 117 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunTree.cs", +"region": { +"startLine": 196, +"startColumn": 45, +"endLine": 196, +"endColumn": 72 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunTree.cs", +"region": { +"startLine": 196, +"startColumn": 94, +"endLine": 196, +"endColumn": 117 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunTree.cs", +"region": { +"startLine": 350, +"startColumn": 45, +"endLine": 350, +"endColumn": 72 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunTree.cs", +"region": { +"startLine": 350, +"startColumn": 94, +"endLine": 350, +"endColumn": 117 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Persistence--netstandard2.0-S6562.json b/analyzers/its/expected/akka.net/Akka.Persistence--netstandard2.0-S6562.json new file mode 100644 index 00000000000..cd1f502134b --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Persistence--netstandard2.0-S6562.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Persistence\Snapshot\LocalSnapshotStore.cs", +"region": { +"startLine": 295, +"startColumn": 66, +"endLine": 295, +"endColumn": 85 +} +} +}, +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Persistence\Snapshot\MemorySnapshotStore.cs", +"region": { +"startLine": 102, +"startColumn": 101, +"endLine": 102, +"endColumn": 130 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Persistence.Sqlite--netstandard2.0-S6562.json b/analyzers/its/expected/akka.net/Akka.Persistence.Sqlite--netstandard2.0-S6562.json new file mode 100644 index 00000000000..b1dc19e74a5 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Persistence.Sqlite--netstandard2.0-S6562.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\contrib\persistence\Akka.Persistence.Sqlite\Snapshot\SqliteSnapshotStore.cs", +"region": { +"startLine": 88, +"startColumn": 29, +"endLine": 88, +"endColumn": 61 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S6562.json b/analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S6562.json new file mode 100644 index 00000000000..0c71566e172 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S6562.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S6562", +"message": "Provide the "DateTimeKind" when creating this object.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Persistence.TCK\Snapshot\SnapshotStoreSpec.cs", +"region": { +"startLine": 119, +"startColumn": 99, +"endLine": 119, +"endColumn": 119 +} +} +} +] +} From 4e2c527089f65122816d8f0159fa3ad0e6bf4156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Mon, 3 Jul 2023 11:44:04 +0200 Subject: [PATCH 07/12] Fix codesmell --- .../src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs index cc3a548aa5c..849349aa2fd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs @@ -36,7 +36,7 @@ protected override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { if (Language.Syntax.ObjectCreationTypeIdentifier(c.Node) is { } identifier - && ValidNames.Any(x => x.Equals(identifier.ValueText, Language.NameComparison)) + && Array.Exists(ValidNames, x => x.Equals(identifier.ValueText, Language.NameComparison)) && IsDateTimeConstructorWithoutKindParameter(c.Node, c.SemanticModel)) { c.ReportIssue(Diagnostic.Create(Rule, c.Node.GetLocation())); From e1516679c9dbefaaf9d2464c02646afc48e6e527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Mon, 3 Jul 2023 11:45:22 +0200 Subject: [PATCH 08/12] Move methods from helpers to extensions and bump coverage --- .../InvocationExpressionSyntaxExtensions.cs | 3 ++ ...bjectCreationExpressionSyntaxExtensions.cs | 3 ++ .../Extensions/SyntaxNodeExtensions.cs | 38 ++++++++++++++++ .../Helpers/CSharpSyntaxHelper.cs | 44 ------------------- .../InvocationExpressionSyntaxExtensions.cs | 3 ++ ...bjectCreationExpressionSyntaxExtensions.cs | 28 ++++++++++++ .../Extensions/SyntaxNodeExtensions.cs | 25 +++++++++++ .../Helpers/VisualBasicSyntaxHelper.cs | 37 ---------------- ...nvocationExpressionSyntaxExtensionsTest.cs | 8 ++++ ...tCreationExpressionSyntaxExtensionsTest.cs | 27 +++++++++--- .../Facade/SyntaxFacadeTest.cs | 8 ++++ .../Helpers/MemberDescriptorTest.cs | 9 +++- .../Rules/AlwaysSetDateTimeKindTest.cs | 1 + .../Trackers/BuilderPatternConditionTest.cs | 9 +++- 14 files changed, 151 insertions(+), 92 deletions(-) create mode 100644 analyzers/src/SonarAnalyzer.VisualBasic/Extensions/ObjectCreationExpressionSyntaxExtensions.cs diff --git a/analyzers/src/SonarAnalyzer.CSharp/Extensions/InvocationExpressionSyntaxExtensions.cs b/analyzers/src/SonarAnalyzer.CSharp/Extensions/InvocationExpressionSyntaxExtensions.cs index 66ea601d50b..ac580ff5b0b 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Extensions/InvocationExpressionSyntaxExtensions.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Extensions/InvocationExpressionSyntaxExtensions.cs @@ -53,5 +53,8 @@ internal static bool TryGetOperands(this InvocationExpressionSyntax invocation, return left is not null && right is not null; } + + internal static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) => + invocation?.Expression.GetIdentifier(); } } diff --git a/analyzers/src/SonarAnalyzer.CSharp/Extensions/ObjectCreationExpressionSyntaxExtensions.cs b/analyzers/src/SonarAnalyzer.CSharp/Extensions/ObjectCreationExpressionSyntaxExtensions.cs index d9e5efbed38..870f64b519d 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Extensions/ObjectCreationExpressionSyntaxExtensions.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Extensions/ObjectCreationExpressionSyntaxExtensions.cs @@ -25,5 +25,8 @@ internal static class ObjectCreationExpressionSyntaxExtensions public static bool IsKnownType(this ObjectCreationExpressionSyntax objectCreation, KnownType knownType, SemanticModel semanticModel) => objectCreation.Type.GetName().EndsWith(knownType.TypeName) && SymbolHelper.IsKnownType(objectCreation, knownType, semanticModel); + + public static SyntaxToken? GetObjectCreationTypeIdentifier(this ObjectCreationExpressionSyntax objectCreation) => + objectCreation?.Type.GetIdentifier(); } } diff --git a/analyzers/src/SonarAnalyzer.CSharp/Extensions/SyntaxNodeExtensions.cs b/analyzers/src/SonarAnalyzer.CSharp/Extensions/SyntaxNodeExtensions.cs index 4bd42b9d2db..bb6d753d912 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Extensions/SyntaxNodeExtensions.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Extensions/SyntaxNodeExtensions.cs @@ -134,6 +134,44 @@ public static SyntaxNode WalkUpParentheses(this SyntaxNode node) return node; } + public static SyntaxToken? GetIdentifier(this SyntaxNode node) => + node switch + { + AliasQualifiedNameSyntax { Alias.Identifier: var identifier } => identifier, + ArrayTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType), + AttributeSyntax { Name: { } name } => GetIdentifier(name), + BaseTypeDeclarationSyntax { Identifier: var identifier } => identifier, + ConstructorDeclarationSyntax { Identifier: var identifier } => identifier, + ConversionOperatorDeclarationSyntax { Type: { } type } => GetIdentifier(type), + DelegateDeclarationSyntax { Identifier: var identifier } => identifier, + DestructorDeclarationSyntax { Identifier: var identifier } => identifier, + EnumMemberDeclarationSyntax { Identifier: var identifier } => identifier, + IdentifierNameSyntax { Identifier: var identifier } => identifier, + IndexerDeclarationSyntax { ThisKeyword: var thisKeyword } => thisKeyword, + InvocationExpressionSyntax + { + Expression: not InvocationExpressionSyntax // We don't want to recurse into nested invocations like: fun()() + } invocation => GetIdentifier(invocation.Expression), + MethodDeclarationSyntax { Identifier: var identifier } => identifier, + MemberBindingExpressionSyntax { Name.Identifier: var identifier } => identifier, + MemberAccessExpressionSyntax { Name.Identifier: var identifier } => identifier, + NamespaceDeclarationSyntax { Name: { } name } => GetIdentifier(name), + NullableTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType), + OperatorDeclarationSyntax { OperatorToken: var operatorToken } => operatorToken, + ParameterSyntax { Identifier: var identifier } => identifier, + PropertyDeclarationSyntax { Identifier: var identifier } => identifier, + PointerTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType), + PredefinedTypeSyntax { Keyword: var keyword } => keyword, + QualifiedNameSyntax { Right.Identifier: var identifier } => identifier, + SimpleNameSyntax { Identifier: var identifier } => identifier, + TypeParameterConstraintClauseSyntax { Name.Identifier: var identifier } => identifier, + TypeParameterSyntax { Identifier: var identifier } => identifier, + UsingDirectiveSyntax { Alias.Name: { } name } => GetIdentifier(name), + VariableDeclaratorSyntax { Identifier: var identifier } => identifier, + { } refType when RefTypeSyntaxWrapper.IsInstance(refType) => GetIdentifier(((RefTypeSyntaxWrapper)refType).Type), + _ => null + }; + /// /// Finds the syntactic complementing of an assignment with tuples. /// diff --git a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs index e4ac6d00090..340f39670c8 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs @@ -185,12 +185,6 @@ public static bool ContainsMethodInvocation(this BaseMethodDeclarationSyntax met } } - public static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) => - invocation == null ? null : GetIdentifier(invocation.Expression); - - public static SyntaxToken? GetObjectCreationTypeIdentifier(this ObjectCreationExpressionSyntax objectCreation) => - objectCreation == null ? null : GetIdentifier(objectCreation.Type); - public static bool IsMethodInvocation(this InvocationExpressionSyntax invocation, KnownType type, string methodName, SemanticModel semanticModel) => invocation.Expression.NameIs(methodName) && semanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && @@ -218,44 +212,6 @@ public static int GetDefaultLabelSectionIndex(this SwitchStatementSyntax node) = public static bool HasBodyOrExpressionBody(this AccessorDeclarationSyntax node) => node.Body != null || node.ExpressionBody() != null; - public static SyntaxToken? GetIdentifier(this SyntaxNode node) => - node switch - { - AliasQualifiedNameSyntax { Alias.Identifier: var identifier } => identifier, - ArrayTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType), - AttributeSyntax { Name: { } name } => GetIdentifier(name), - BaseTypeDeclarationSyntax { Identifier: var identifier } => identifier, - ConstructorDeclarationSyntax { Identifier: var identifier } => identifier, - ConversionOperatorDeclarationSyntax { Type: { } type } => GetIdentifier(type), - DelegateDeclarationSyntax { Identifier: var identifier } => identifier, - DestructorDeclarationSyntax { Identifier: var identifier } => identifier, - EnumMemberDeclarationSyntax { Identifier: var identifier } => identifier, - IdentifierNameSyntax { Identifier: var identifier } => identifier, - IndexerDeclarationSyntax { ThisKeyword: var thisKeyword } => thisKeyword, - InvocationExpressionSyntax - { - Expression: not InvocationExpressionSyntax // We don't want to recurse into nested invocations like: fun()() - } invocation => GetIdentifier(invocation.Expression), - MethodDeclarationSyntax { Identifier: var identifier } => identifier, - MemberBindingExpressionSyntax { Name.Identifier: var identifier } => identifier, - MemberAccessExpressionSyntax { Name.Identifier: var identifier } => identifier, - NamespaceDeclarationSyntax { Name: { } name } => GetIdentifier(name), - NullableTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType), - OperatorDeclarationSyntax { OperatorToken: var operatorToken } => operatorToken, - ParameterSyntax { Identifier: var identifier } => identifier, - PropertyDeclarationSyntax { Identifier: var identifier } => identifier, - PointerTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType), - PredefinedTypeSyntax { Keyword: var keyword } => keyword, - QualifiedNameSyntax { Right.Identifier: var identifier } => identifier, - SimpleNameSyntax { Identifier: var identifier } => identifier, - TypeParameterConstraintClauseSyntax { Name.Identifier: var identifier } => identifier, - TypeParameterSyntax { Identifier: var identifier } => identifier, - UsingDirectiveSyntax { Alias.Name: { } name } => GetIdentifier(name), - VariableDeclaratorSyntax { Identifier: var identifier } => identifier, - { } refType when RefTypeSyntaxWrapper.IsInstance(refType) => GetIdentifier(((RefTypeSyntaxWrapper)refType).Type), - _ => null - }; - public static string GetName(this SyntaxNode node) => node.GetIdentifier()?.ValueText ?? string.Empty; diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/InvocationExpressionSyntaxExtensions.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/InvocationExpressionSyntaxExtensions.cs index 34e07537b20..696ca352123 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/InvocationExpressionSyntaxExtensions.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/InvocationExpressionSyntaxExtensions.cs @@ -46,5 +46,8 @@ internal static bool TryGetOperands(this InvocationExpressionSyntax invocation, return left is not null && right is not null; } + + internal static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) => + invocation?.Expression.GetIdentifier(); } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/ObjectCreationExpressionSyntaxExtensions.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/ObjectCreationExpressionSyntaxExtensions.cs new file mode 100644 index 00000000000..d17d1752fc6 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/ObjectCreationExpressionSyntaxExtensions.cs @@ -0,0 +1,28 @@ +/* + * 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.Extensions +{ + internal static class ObjectCreationExpressionSyntaxExtensions + { + public static SyntaxToken? GetObjectCreationTypeIdentifier(this ObjectCreationExpressionSyntax objectCreation) => + objectCreation?.Type.GetIdentifier(); + } +} diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/SyntaxNodeExtensions.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/SyntaxNodeExtensions.cs index ab1a899f622..0508a7b7ee3 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/SyntaxNodeExtensions.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Extensions/SyntaxNodeExtensions.cs @@ -74,6 +74,31 @@ static bool TakesExpressionTree(SymbolInfo info) } } + public static SyntaxToken? GetIdentifier(this SyntaxNode node) => + node?.RemoveParentheses() switch + { + AttributeSyntax x => x.Name?.GetIdentifier(), + ClassBlockSyntax x => x.ClassStatement.Identifier, + ClassStatementSyntax x => x.Identifier, + IdentifierNameSyntax x => x.Identifier, + MemberAccessExpressionSyntax x => x.Name.Identifier, + MethodBlockSyntax x => x.SubOrFunctionStatement?.GetIdentifier(), + MethodStatementSyntax x => x.Identifier, + ModuleBlockSyntax x => x.ModuleStatement.Identifier, + EnumStatementSyntax x => x.Identifier, + EnumMemberDeclarationSyntax x => x.Identifier, + InvocationExpressionSyntax x => x.Expression?.GetIdentifier(), + ModifiedIdentifierSyntax x => x.Identifier, + PredefinedTypeSyntax x => x.Keyword, + ParameterSyntax x => x.Identifier?.GetIdentifier(), + PropertyStatementSyntax x => x.Identifier, + SimpleArgumentSyntax x => x.NameColonEquals?.Name.Identifier, + SimpleNameSyntax x => x.Identifier, + StructureBlockSyntax x => x.StructureStatement.Identifier, + QualifiedNameSyntax x => x.Right.Identifier, + _ => null, + }; + /// /// Returns the left hand side of a conditional access expression. Returns c in case like a?.b?[0].c?.d.e?.f if d is passed. /// diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs index 90e4310ae03..aa66680204d 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Helpers/VisualBasicSyntaxHelper.cs @@ -104,18 +104,6 @@ public static bool IsAnyKind(this SyntaxTrivia syntaxTrivia, params SyntaxKind[] public static bool AnyOfKind(this IEnumerable nodes, SyntaxKind kind) => nodes.Any(n => n.RawKind == (int)kind); - public static SyntaxToken? GetMethodCallIdentifier(this InvocationExpressionSyntax invocation) => - invocation == null - || invocation.Expression == null - ? null - : GetIdentifier(invocation.Expression); - - public static SyntaxToken? GetObjectCreationTypeIdentifier(this ObjectCreationExpressionSyntax objectCreation) => - objectCreation == null - || objectCreation.Type == null - ? null - : GetIdentifier(objectCreation.Type); - public static bool IsMethodInvocation(this InvocationExpressionSyntax expression, KnownType type, string methodName, SemanticModel semanticModel) => semanticModel.GetSymbolInfo(expression).Symbol is IMethodSymbol methodSymbol && methodSymbol.IsInType(type) && @@ -150,31 +138,6 @@ private static bool IsOn(this ExpressionSyntax expression, SyntaxKind onKind) } } - public static SyntaxToken? GetIdentifier(this SyntaxNode node) => - node?.RemoveParentheses() switch - { - AttributeSyntax x => x.Name?.GetIdentifier(), - ClassBlockSyntax x => x.ClassStatement.Identifier, - ClassStatementSyntax x => x.Identifier, - IdentifierNameSyntax x => x.Identifier, - MemberAccessExpressionSyntax x => x.Name.Identifier, - MethodBlockSyntax x => x.SubOrFunctionStatement?.GetIdentifier(), - MethodStatementSyntax x => x.Identifier, - ModuleBlockSyntax x => x.ModuleStatement.Identifier, - EnumStatementSyntax x => x.Identifier, - EnumMemberDeclarationSyntax x => x.Identifier, - InvocationExpressionSyntax x => x.Expression?.GetIdentifier(), - ModifiedIdentifierSyntax x => x.Identifier, - PredefinedTypeSyntax x => x.Keyword, - ParameterSyntax x => x.Identifier?.GetIdentifier(), - PropertyStatementSyntax x => x.Identifier, - SimpleArgumentSyntax x => x.NameColonEquals?.Name.Identifier, - SimpleNameSyntax x => x.Identifier, - StructureBlockSyntax x => x.StructureStatement.Identifier, - QualifiedNameSyntax x => x.Right.Identifier, - _ => null, - }; - public static string GetName(this SyntaxNode expression) => expression.GetIdentifier()?.ValueText ?? string.Empty; diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/InvocationExpressionSyntaxExtensionsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/InvocationExpressionSyntaxExtensionsTest.cs index 11d0c2a4e3b..e602b7af4f0 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/InvocationExpressionSyntaxExtensionsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/InvocationExpressionSyntaxExtensionsTest.cs @@ -132,6 +132,14 @@ public int M() right.Should().BeNull(); } + [TestMethod] + public void GetMethodCallIdentifier_Null_CS() => + SyntaxNodeExtensionsCS.GetMethodCallIdentifier(null).Should().BeNull(); + + [TestMethod] + public void GetMethodCallIdentifier_Null_VB() => + SyntaxNodeExtensionsVB.GetMethodCallIdentifier(null).Should().BeNull(); + private static SyntaxNode NodeBetweenMarkers(string code, string language) { var position = code.IndexOf("$$"); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/ObjectCreationExpressionSyntaxExtensionsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/ObjectCreationExpressionSyntaxExtensionsTest.cs index 54ca8fb1ee0..51aa1e0edbc 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/ObjectCreationExpressionSyntaxExtensionsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/ObjectCreationExpressionSyntaxExtensionsTest.cs @@ -18,9 +18,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; +extern alias csharp; +extern alias vbnet; + using SonarAnalyzer.Extensions; +using CS = Microsoft.CodeAnalysis.CSharp; +using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; +using SyntaxNodeExtensionsCS = csharp::SonarAnalyzer.Extensions.ObjectCreationExpressionSyntaxExtensions; +using SyntaxNodeExtensionsVB = vbnet::SonarAnalyzer.Extensions.ObjectCreationExpressionSyntaxExtensions; namespace SonarAnalyzer.UnitTest.Extensions { @@ -36,15 +41,23 @@ public void IsKnownType_ChecksCtorType(string code, bool expectedResult) { var compilation = CreateCompilation(code); var syntaxTree = compilation.SyntaxTrees.First(); - var objectCreation = syntaxTree.First(); + var objectCreation = syntaxTree.First(); objectCreation.IsKnownType(KnownType.System_DateTime, compilation.GetSemanticModel(syntaxTree)).Should().Be(expectedResult); } - private static CSharpCompilation CreateCompilation(string code) => - CSharpCompilation.Create("TempAssembly.dll") - .AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)) + [TestMethod] + public void GetObjectCreationTypeIdentifier_Null_CS() => + SyntaxNodeExtensionsCS.GetObjectCreationTypeIdentifier(null).Should().BeNull(); + + [TestMethod] + public void GetObjectCreationTypeIdentifier_Null_VB() => + SyntaxNodeExtensionsVB.GetObjectCreationTypeIdentifier(null).Should().BeNull(); + + private static CS.CSharpCompilation CreateCompilation(string code) => + CS.CSharpCompilation.Create("TempAssembly.dll") + .AddSyntaxTrees(CS.CSharpSyntaxTree.ParseText(code)) .AddReferences(MetadataReferenceFacade.ProjectDefaultReferences) - .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + .WithOptions(new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Facade/SyntaxFacadeTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Facade/SyntaxFacadeTest.cs index e413aef6b56..520c9c5d2fb 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Facade/SyntaxFacadeTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Facade/SyntaxFacadeTest.cs @@ -46,6 +46,14 @@ public void InvocationIdentifier_Null_CS() => public void InvocationIdentifier_Null_VB() => vb.InvocationIdentifier(null).Should().BeNull(); + [TestMethod] + public void ObjectCreationTypeIdentifier_Null_CS() => + cs.ObjectCreationTypeIdentifier(null).Should().BeNull(); + + [TestMethod] + public void ObjectCreationTypeIdentifier_Null_VB() => + vb.ObjectCreationTypeIdentifier(null).Should().BeNull(); + [TestMethod] public void InvocationIdentifier_UnexpectedTypeThrows_CS() => cs.Invoking(x => x.InvocationIdentifier(CS.SyntaxFactory.IdentifierName("ThisIsNotInvocation"))).Should().Throw(); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MemberDescriptorTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MemberDescriptorTest.cs index 436bcb3b5f6..8fd43ccf701 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MemberDescriptorTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MemberDescriptorTest.cs @@ -18,9 +18,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +extern alias csharp; +extern alias vbnet; + using Moq; using CSharpSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; +using CSharpSyntaxNodeExtensions = csharp::SonarAnalyzer.Extensions.SyntaxNodeExtensions; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; +using VBSyntaxNodeExtensions = vbnet::SonarAnalyzer.Extensions.SyntaxNodeExtensions; namespace SonarAnalyzer.UnitTest.Helpers { @@ -408,11 +413,11 @@ private static InvocationContext CreateContextForMethod(string typeAndMethodName IEnumerable<(SyntaxNode node, string name)> GetCSharpNodes() => snippet.GetNodes() - .Select(n => ((SyntaxNode)n, CSharpSyntaxHelper.GetIdentifier(n.Expression)?.ValueText)); + .Select(n => ((SyntaxNode)n, CSharpSyntaxNodeExtensions.GetIdentifier(n.Expression)?.ValueText)); IEnumerable<(SyntaxNode node, string name)> GetVbNodes() => snippet.GetNodes() - .Select(n => ((SyntaxNode)n, VisualBasicSyntaxHelper.GetIdentifier(n.Expression)?.ValueText)); + .Select(n => ((SyntaxNode)n, VBSyntaxNodeExtensions.GetIdentifier(n.Expression)?.ValueText)); } private static void CheckExactMatchOnly_OverridesAreNotMatched(SnippetCompiler snippet) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs index 4ac8fd1433d..cbc813da8f7 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -45,6 +45,7 @@ public void AlwaysSetDateTimeKind_CSharp9() => using System; DateTime dateTime = new(1994, 07, 05, 16, 23, 00, 42, 42); // Noncompliant + dateTime = new(1623, DateTimeKind.Unspecified); // Compliant """) .WithTopLevelStatements() .Verify(); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Trackers/BuilderPatternConditionTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Trackers/BuilderPatternConditionTest.cs index 73e94a2d344..fb2a7570b77 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Trackers/BuilderPatternConditionTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Trackers/BuilderPatternConditionTest.cs @@ -18,10 +18,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +extern alias csharp; +extern alias vbnet; + using BuilderPatternDescriptorCS = SonarAnalyzer.Helpers.BuilderPatternDescriptor; using BuilderPatternDescriptorVB = SonarAnalyzer.Helpers.BuilderPatternDescriptor; using CSharpSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; +using CSharpSyntaxNodeExtensions = csharp::SonarAnalyzer.Extensions.SyntaxNodeExtensions; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; +using VBSyntaxNodeExtensions = vbnet::SonarAnalyzer.Extensions.SyntaxNodeExtensions; namespace SonarAnalyzer.UnitTest.Helpers { @@ -203,11 +208,11 @@ End Function private static SyntaxNode FindMethodInvocation_CS(SyntaxTree tree, string name) => tree.GetRoot().DescendantNodes() .OfType() - .Single(x => CSharpSyntaxHelper.GetIdentifier(x.Expression)?.ValueText == name); + .Single(x => CSharpSyntaxNodeExtensions.GetIdentifier(x.Expression)?.ValueText == name); private static SyntaxNode FindMethodInvocation_VB(SyntaxTree tree, string name) => tree.GetRoot().DescendantNodes() .OfType() - .Single(x => VisualBasicSyntaxHelper.GetIdentifier(x.Expression)?.ValueText == name); + .Single(x => VBSyntaxNodeExtensions.GetIdentifier(x.Expression)?.ValueText == name); } } From 42a28105754621139d960028d8c9072eef156fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Mon, 3 Jul 2023 17:21:47 +0200 Subject: [PATCH 09/12] Address comments --- .../src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs | 2 +- .../SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs | 1 + .../SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs index 849349aa2fd..900699d5fb1 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/AlwaysSetDateTimeKindBase.cs @@ -35,7 +35,7 @@ protected AlwaysSetDateTimeKindBase() : base(DiagnosticId) { } protected override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { - if (Language.Syntax.ObjectCreationTypeIdentifier(c.Node) is { } identifier + if (Language.Syntax.ObjectCreationTypeIdentifier(c.Node) is { IsMissing: false } identifier && Array.Exists(ValidNames, x => x.Equals(identifier.ValueText, Language.NameComparison)) && IsDateTimeConstructorWithoutKindParameter(c.Node, c.SemanticModel)) { diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs index 4d1e2c236a2..1b58672e000 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs @@ -27,6 +27,7 @@ public void Compliant() dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Utc); dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar(), DateTimeKind.Unspecified); dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Unspecified); + dt = new System.(1623) // Error [CS1001, CS1002] } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb index 2dacf517bba..11f7e7fbfb2 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb @@ -19,7 +19,7 @@ Public Class Program dt = New System.DateTime() ' Noncompliant End Sub - Public Sub Compiant() + Public Sub Compliant() Dim dt = New DateTime(1623, DateTimeKind.Unspecified) dt = New DateTime(1994, 7, 5, 16, 23, 0, DateTimeKind.Local) dt = New DateTime(1994, 7, 5, 16, 23, 0, 42, New GregorianCalendar(), DateTimeKind.Unspecified) From 81846271c79273add0ee5e1f17371b90442035e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Tue, 4 Jul 2023 13:09:23 +0200 Subject: [PATCH 10/12] Address review comments --- .../SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs | 1 + .../SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs | 3 ++- .../SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs index cbc813da8f7..35de1c3cd5c 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -45,6 +45,7 @@ public void AlwaysSetDateTimeKind_CSharp9() => using System; DateTime dateTime = new(1994, 07, 05, 16, 23, 00, 42, 42); // Noncompliant + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dateTime = new(1623, DateTimeKind.Unspecified); // Compliant """) .WithTopLevelStatements() diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs index 1b58672e000..3e5bc1837b0 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.cs @@ -7,6 +7,7 @@ public class Program public void Noncompliant() { var dt = new DateTime(); // Noncompliant {{Provide the "DateTimeKind" when creating this object.}} + // ^^^^^^^^^^^^^^ dt = new DateTime(1623); // Noncompliant dt = new DateTime(1994, 07, 05); // Noncompliant dt = new DateTime(1994, 07, 05, new GregorianCalendar()); // Noncompliant @@ -27,7 +28,7 @@ public void Compliant() dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Utc); dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, new GregorianCalendar(), DateTimeKind.Unspecified); dt = new DateTime(1994, 07, 05, 16, 23, 00, 42, DateTimeKind.Unspecified); - dt = new System.(1623) // Error [CS1001, CS1002] + dt = new System.(1623); // Error [CS1001] } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb index 11f7e7fbfb2..62ed76528c4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/AlwaysSetDateTimeKind.vb @@ -5,6 +5,7 @@ Public Class Program Public Sub Noncompliant() Dim dt = New DateTime() ' Noncompliant {{Provide the "DateTimeKind" when creating this object.}} + ' ^^^^^^^^^^^^^^ dt = New Date() ' Noncompliant dt = New dATEtIME() ' Noncompliant dt = New DateTime(1623) ' Noncompliant From e76bc205f0863e5ddd94f5e9c86d4c642aaaa91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Tue, 4 Jul 2023 17:08:27 +0200 Subject: [PATCH 11/12] Address comments --- .../src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs | 2 +- .../SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs | 2 +- .../Rules/AlwaysSetDateTimeKindTest.cs | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs index 2f722d59556..148a43a36c4 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/AlwaysSetDateTimeKind.cs @@ -27,7 +27,7 @@ public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase SyntaxKind.ObjectCreationExpression; - protected override string[] ValidNames { get; } = new[] { "DateTime" }; + protected override string[] ValidNames { get; } = new[] { nameof(DateTime) }; protected override void Initialize(SonarAnalysisContext context) { diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs index 05c2738e3e3..fb213230fa4 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/AlwaysSetDateTimeKind.cs @@ -27,5 +27,5 @@ public sealed class AlwaysSetDateTimeKind : AlwaysSetDateTimeKindBase SyntaxKind.ObjectCreationExpression; - protected override string[] ValidNames { get; } = new[] { "DateTime", "Date" }; + protected override string[] ValidNames { get; } = new[] { nameof(DateTime), "Date" }; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs index 35de1c3cd5c..f519abaf039 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -36,8 +36,6 @@ public void AlwaysSetDateTimeKind_CS() => public void AlwaysSetDateTimeKind_VB() => new VerifierBuilder().AddPaths("AlwaysSetDateTimeKind.vb").Verify(); -#if NET - [TestMethod] public void AlwaysSetDateTimeKind_CSharp9() => builderCS.AddSnippet( @@ -50,7 +48,4 @@ public void AlwaysSetDateTimeKind_CSharp9() => """) .WithTopLevelStatements() .Verify(); - -#endif - } From 6b5d9f82fa89e9b042a62aec8c1f5be534461af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Caba=20=C5=A0agi?= Date: Tue, 4 Jul 2023 17:24:36 +0200 Subject: [PATCH 12/12] Run the CSharp9 test only for .NET as the CTOR overload does not exist for .NET framework --- .../Rules/AlwaysSetDateTimeKindTest.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs index f519abaf039..35de1c3cd5c 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/AlwaysSetDateTimeKindTest.cs @@ -36,6 +36,8 @@ public void AlwaysSetDateTimeKind_CS() => public void AlwaysSetDateTimeKind_VB() => new VerifierBuilder().AddPaths("AlwaysSetDateTimeKind.vb").Verify(); +#if NET + [TestMethod] public void AlwaysSetDateTimeKind_CSharp9() => builderCS.AddSnippet( @@ -48,4 +50,7 @@ public void AlwaysSetDateTimeKind_CSharp9() => """) .WithTopLevelStatements() .Verify(); + +#endif + }