From 695358bf1d400b62a3e023cbc4b99b9564eb2f96 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 6 Feb 2023 20:06:12 +0100 Subject: [PATCH 01/31] Add KnowReference and tests --- .../Helpers/CompilationExtensions.cs | 3 + .../Helpers/KnownReference.cs | 65 +++++ .../Helpers/KnownReferenceExtensions.cs | 30 +++ .../Extensions/KnownReferenceTests.cs | 222 ++++++++++++++++++ 4 files changed, 320 insertions(+) create mode 100644 analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs create mode 100644 analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs index 5f1a42e0849..aa0c1aef20d 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs @@ -31,5 +31,8 @@ public static class CompilationExtensions // There's no direct way of checking compilation target framework yet (09/2020). // See https://github.com/dotnet/roslyn/issues/3798 compilation.ObjectType.ContainingAssembly.Name == "mscorlib"; + + public static bool References(this Compilation compilation, KnownReference reference) + => reference.IsReferenced(compilation); } } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs new file mode 100644 index 00000000000..c9cbde674dc --- /dev/null +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -0,0 +1,65 @@ +/* + * 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.Helpers +{ + public sealed class KnownReference + { + private readonly Func, bool> predicate; + + internal KnownReference(Func predicate) : this(Any(predicate)) + { + } + + internal KnownReference(Func, bool> predicate) + { + this.predicate = predicate; + } + + public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen(new Version(2, 0))))); + + internal static Func NameIs(string name) => + new(x => x.Name.Equals(name)); + + internal static Func StartsWith(string name) => + new(x => x.Name.StartsWith(name)); + + internal static Func EndsWith(string name) => + new(x => x.Name.EndsWith(name)); + + internal static Func Contains(string name) => + new(x => x.Name.Contains(name)); + + internal static Func VersionLowerThen(Version version) => + new(x => x.Version < version); + + internal static Func VersionGreaterOrEqual(Version version) => + new(x => x.Version >= version); + + internal static Func VersionBetween(Version from, Version to) => + new(x => x.Version >= from && x.Version <= to); + + internal static Func, bool> Any(Func predicate) => + new(identities => identities.Any(predicate)); + + public bool IsReferenced(Compilation compilation) => + predicate(compilation.ReferencedAssemblyNames); + } +} diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs new file mode 100644 index 00000000000..98c64906672 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs @@ -0,0 +1,30 @@ +/* + * 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.Helpers; + +internal static class KnownReferenceExtensions +{ + internal static Func And(this Func predicate, Func and) + => identity => predicate(identity) && and(identity); + + internal static Func Or(this Func predicate, Func or) + => identity => predicate(identity) || or(identity); +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs new file mode 100644 index 00000000000..82c3665037f --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -0,0 +1,222 @@ +/* + * 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 Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SonarAnalyzer.Common; +using SonarAnalyzer.Helpers; +using static SonarAnalyzer.Helpers.KnownReference; + +namespace SonarAnalyzer.UnitTest; + +[TestClass] +public class KnownReferenceTests +{ + [DataTestMethod] + [DataRow("Test", true)] + [DataRow("test", false)] + [DataRow("TEST", false)] + [DataRow("MyTest", false)] + [DataRow("TestMy", false)] + [DataRow("MyTestMy", false)] + [DataRow("MyTESTMy", false)] + [DataRow("Without", false)] + public void NameIs_Test(string name, bool expected) + { + var sut = new KnownReference(NameIs("Test")); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity(name); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("Test", true)] + [DataRow("test", false)] + [DataRow("TEST", false)] + [DataRow("MyTest", true)] + [DataRow("TestMy", true)] + [DataRow("MyTestMy", true)] + [DataRow("MyTESTMy", false)] + [DataRow("Without", false)] + public void NameContains_Test(string name, bool expected) + { + var sut = new KnownReference(Contains("Test")); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity(name); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("Test", true)] + [DataRow("test", false)] + [DataRow("TEST", false)] + [DataRow("MyTest", false)] + [DataRow("TestMy", true)] + [DataRow("MyTestMy", false)] + [DataRow("MyTESTMy", false)] + [DataRow("Without", false)] + public void NameStartsWith_Test(string name, bool expected) + { + var sut = new KnownReference(StartsWith("Test")); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity(name); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("Test", true)] + [DataRow("test", false)] + [DataRow("TEST", false)] + [DataRow("MyTest", true)] + [DataRow("TestMy", false)] + [DataRow("MyTestMy", false)] + [DataRow("MyTESTMy", false)] + [DataRow("Without", false)] + public void NameEndsWith_Test(string name, bool expected) + { + var sut = new KnownReference(EndsWith("Test")); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity(name); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("1.0.0.0", false)] + [DataRow("1.9.9.99", false)] + [DataRow("2.0.0.0", true)] + [DataRow("2.0.0.1", true)] + [DataRow("2.1.0.0", true)] + [DataRow("3.1.0.0", true)] + public void Version_GreaterOrEqual_2_0(string version, bool expected) + { + var sut = new KnownReference(VersionGreaterOrEqual(new Version(2, 0))); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity("assemblyName", new Version(version)); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("1.0.0.0", true)] + [DataRow("1.9.9.99", true)] + [DataRow("2.0.0.0", false)] + [DataRow("2.0.0.1", false)] + [DataRow("2.1.0.0", false)] + [DataRow("3.1.0.0", false)] + public void Version_LowerThen_2_0(string version, bool expected) + { + var sut = new KnownReference(VersionLowerThen(new Version(2, 0))); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity("assemblyName", new Version(version)); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("1.0.0.0", false)] + [DataRow("1.9.9.99", false)] + [DataRow("2.0.0.0", true)] + [DataRow("2.0.0.1", true)] + [DataRow("2.1.0.0", true)] + [DataRow("3.1.0.0", true)] + [DataRow("3.4.9.99", true)] + [DataRow("3.5.0.0", true)] + [DataRow("3.5.0.1", false)] + [DataRow("10.0.0.0", false)] + public void Version_Between_2_0_and_3_5(string version, bool expected) + { + var sut = new KnownReference(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity("assemblyName", new Version(version)); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("Test", "1.0.0.0", false)] + [DataRow("Test", "1.9.9.99", false)] + [DataRow("TestMy", "2.0.0.0", true)] + [DataRow("MyTest", "2.0.0.0", false)] + [DataRow("TestMy", "3.5.0.0", true)] + [DataRow("TestMy", "3.5.0.1", false)] + [DataRow("Test", "10.0.0.0", false)] + public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string name, string version, bool expected) + { + var sut = new KnownReference(StartsWith("Test").And(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0)))); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity(name, new Version(version)); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [DataTestMethod] + [DataRow("Start", true)] + [DataRow("End", true)] + [DataRow("StartOrEnd", true)] + [DataRow("StartTest", true)] + [DataRow("TestEnd", true)] + [DataRow("EndStart", false)] + [DataRow("EndSomething", false)] + [DataRow("SomethingStart", false)] + public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expected) + { + var sut = new KnownReference(StartsWith("Start").Or(EndsWith("End"))); + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity(name); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [TestMethod] + public void XUnitAssert_2_4() + { + var compilation = SolutionBuilder.Create() + .AddProject(AnalyzerLanguage.CSharp) + .AddReferences(NuGetMetadataReference.XunitFramework("2.4.2")) + .AddSnippet("// Empty file") + .GetCompilation(); + compilation.References(XUnit_Assert).Should().BeTrue(); + } + + [TestMethod] + public void XUnitAssert_1_9() + { + var compilation = SolutionBuilder.Create() + .AddProject(AnalyzerLanguage.CSharp) + .AddReferences(NuGetMetadataReference.XunitFrameworkV1) + .AddSnippet("// Empty file") + .GetCompilation(); + compilation.References(XUnit_Assert).Should().BeTrue(); + } + + [TestMethod] + public void XUnitAssert_NoReference() + { + var compilation = SolutionBuilder.Create() + .AddProject(AnalyzerLanguage.CSharp) + .AddSnippet("// Empty file") + .GetCompilation(); + compilation.References(XUnit_Assert).Should().BeFalse(); + } +} From 7547b0321adc87d802865ea052e429983ffb64be Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 6 Feb 2023 21:06:54 +0100 Subject: [PATCH 02/31] Fix code smell (parameter names) --- .../Helpers/KnownReferenceExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs index 98c64906672..23873c6c031 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs @@ -22,9 +22,9 @@ namespace SonarAnalyzer.Helpers; internal static class KnownReferenceExtensions { - internal static Func And(this Func predicate, Func and) - => identity => predicate(identity) && and(identity); + internal static Func And(this Func @this, Func predicate) + => identity => @this(identity) && predicate(identity); - internal static Func Or(this Func predicate, Func or) - => identity => predicate(identity) || or(identity); + internal static Func Or(this Func @this, Func predicate) + => identity => @this(identity) || predicate(identity); } From 6b5d8f4acdc636c2f793c6aa44471a777f366d73 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 10:49:12 +0100 Subject: [PATCH 03/31] Add overloads for version passed as string. --- .../SonarAnalyzer.Common/Helpers/KnownReference.cs | 11 ++++++++++- .../Extensions/KnownReferenceTests.cs | 12 +++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index c9cbde674dc..3655bccd8f8 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -33,7 +33,7 @@ internal KnownReference(Func, bool> predicate) this.predicate = predicate; } - public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen(new Version(2, 0))))); + public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen("2.0")))); internal static Func NameIs(string name) => new(x => x.Name.Equals(name)); @@ -47,12 +47,21 @@ internal KnownReference(Func, bool> predicate) internal static Func Contains(string name) => new(x => x.Name.Contains(name)); + internal static Func VersionLowerThen(string version) => + VersionLowerThen(Version.Parse(version)); + internal static Func VersionLowerThen(Version version) => new(x => x.Version < version); + internal static Func VersionGreaterOrEqual(string version) => + VersionGreaterOrEqual(Version.Parse(version)); + internal static Func VersionGreaterOrEqual(Version version) => new(x => x.Version >= version); + internal static Func VersionBetween(string from, string to) => + VersionBetween(Version.Parse(from), Version.Parse(to)); + internal static Func VersionBetween(Version from, Version to) => new(x => x.Version >= from && x.Version <= to); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index 82c3665037f..bfc26a52abd 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -110,10 +110,12 @@ public void NameEndsWith_Test(string name, bool expected) [DataRow("3.1.0.0", true)] public void Version_GreaterOrEqual_2_0(string version, bool expected) { - var sut = new KnownReference(VersionGreaterOrEqual(new Version(2, 0))); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var sut = new KnownReference(VersionGreaterOrEqual(new Version(2, 0))); + sut.IsReferenced(compilation.Object).Should().Be(expected); + sut = new KnownReference(VersionGreaterOrEqual("2.0")); sut.IsReferenced(compilation.Object).Should().Be(expected); } @@ -126,10 +128,12 @@ public void Version_GreaterOrEqual_2_0(string version, bool expected) [DataRow("3.1.0.0", false)] public void Version_LowerThen_2_0(string version, bool expected) { - var sut = new KnownReference(VersionLowerThen(new Version(2, 0))); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var sut = new KnownReference(VersionLowerThen(new Version(2, 0))); + sut.IsReferenced(compilation.Object).Should().Be(expected); + sut = new KnownReference(VersionLowerThen("2.0")); sut.IsReferenced(compilation.Object).Should().Be(expected); } @@ -146,10 +150,12 @@ public void Version_LowerThen_2_0(string version, bool expected) [DataRow("10.0.0.0", false)] public void Version_Between_2_0_and_3_5(string version, bool expected) { - var sut = new KnownReference(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var sut = new KnownReference(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); + sut.IsReferenced(compilation.Object).Should().Be(expected); + sut = new KnownReference(VersionBetween("2.0.0.0", "3.5.0.0")); sut.IsReferenced(compilation.Object).Should().Be(expected); } From 5f39f96c4bb1075ec540347dce6b65ee00a7008f Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 13:43:26 +0100 Subject: [PATCH 04/31] Add support for public key checks --- .../Helpers/KnownReference.cs | 15 ++++++ .../Extensions/KnownReferenceTests.cs | 48 ++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index 3655bccd8f8..be509b40a1d 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -65,6 +65,21 @@ internal KnownReference(Func, bool> predicate) internal static Func VersionBetween(Version from, Version to) => new(x => x.Version >= from && x.Version <= to); + internal static Func OptionalPublicKeyTokenIs(string key) => + new(x => !x.HasPublicKey || PublicKeyEqualHex(x, key)); + + internal static Func PublicKeyTokenIs(string key) => + new(x => x.HasPublicKey && PublicKeyEqualHex(x, key)); + + private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexString) + { + var normalizedHexString = hexString.Replace("-", string.Empty); + return ArraysEqual(identity.PublicKeyToken.ToArray(), normalizedHexString) || ArraysEqual(identity.PublicKey.ToArray(), normalizedHexString); + + static bool ArraysEqual(byte[] key, string hexString) => + BitConverter.ToString(key).Replace("-", string.Empty).Equals(hexString, StringComparison.OrdinalIgnoreCase); + } + internal static Func, bool> Any(Func predicate) => new(identities => identities.Any(predicate)); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index bfc26a52abd..67ea11c115d 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -18,10 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SonarAnalyzer.Common; -using SonarAnalyzer.Helpers; using static SonarAnalyzer.Helpers.KnownReference; namespace SonarAnalyzer.UnitTest; @@ -159,6 +157,52 @@ public void Version_Between_2_0_and_3_5(string version, bool expected) sut.IsReferenced(compilation.Object).Should().Be(expected); } + [DataTestMethod] + [DataRow("c5b62af9de6d7244", true)] + [DataRow("C5B62AF9DE6D7244", true)] + [DataRow("c5-b6-2a-f9-de-6d-72-44", true)] + [DataRow( + "002400000480000094000000060200000024000052534131000400000100010081b4345a022cc0f4b42bdc795a5a7a1623c1e58dc2246645d751ad41ba98f2749dc5c4e0da3a9e09febcb2cd5b088a0f" + + "041f8ac24b20e736d8ae523061733782f9c4cd75b44f17a63714aced0b29a59cd1ce58d8e10ccdb6012c7098c39871043b7241ac4ab9f6b34f183db716082cd57c1ff648135bece256357ba735e67dc6", true)] + [DataRow("AA-68-91-16-d3-a4-ae-33", false)] + public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expected) + { + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity("assemblyName", publicKeyOrToken: ImmutableArray.Create( + 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, + 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x81, 0xb4, 0x34, 0x5a, 0x02, 0x2c, 0xc0, 0xf4, 0xb4, 0x2b, 0xdc, 0x79, 0x5a, 0x5a, 0x7a, 0x16, 0x23, 0xc1, + 0xe5, 0x8d, 0xc2, 0x24, 0x66, 0x45, 0xd7, 0x51, 0xad, 0x41, 0xba, 0x98, 0xf2, 0x74, 0x9d, 0xc5, 0xc4, 0xe0, 0xda, 0x3a, 0x9e, 0x09, 0xfe, 0xbc, 0xb2, + 0xcd, 0x5b, 0x08, 0x8a, 0x0f, 0x04, 0x1f, 0x8a, 0xc2, 0x4b, 0x20, 0xe7, 0x36, 0xd8, 0xae, 0x52, 0x30, 0x61, 0x73, 0x37, 0x82, 0xf9, 0xc4, 0xcd, 0x75, + 0xb4, 0x4f, 0x17, 0xa6, 0x37, 0x14, 0xac, 0xed, 0x0b, 0x29, 0xa5, 0x9c, 0xd1, 0xce, 0x58, 0xd8, 0xe1, 0x0c, 0xcd, 0xb6, 0x01, 0x2c, 0x70, 0x98, 0xc3, + 0x98, 0x71, 0x04, 0x3b, 0x72, 0x41, 0xac, 0x4a, 0xb9, 0xf6, 0xb3, 0x4f, 0x18, 0x3d, 0xb7, 0x16, 0x08, 0x2c, 0xd5, 0x7c, 0x1f, 0xf6, 0x48, 0x13, 0x5b, + 0xec, 0xe2, 0x56, 0x35, 0x7b, 0xa7, 0x35, 0xe6, 0x7d, 0xc6), hasPublicKey: true); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var sut = new KnownReference(PublicKeyTokenIs(publicKeyToken)); + sut.IsReferenced(compilation.Object).Should().Be(expected); + sut = new KnownReference(OptionalPublicKeyTokenIs(publicKeyToken)); + sut.IsReferenced(compilation.Object).Should().Be(expected); + } + + [TestMethod] + public void PublicKeyTokenIs_FailsWhenKeyIsMissing() + { + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var sut = new KnownReference(PublicKeyTokenIs("c5b62af9de6d7244")); + sut.IsReferenced(compilation.Object).Should().BeFalse(); + } + + [TestMethod] + public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() + { + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var sut = new KnownReference(OptionalPublicKeyTokenIs("c5b62af9de6d7244")); + sut.IsReferenced(compilation.Object).Should().BeTrue(); + } + [DataTestMethod] [DataRow("Test", "1.0.0.0", false)] [DataRow("Test", "1.9.9.99", false)] From c739408743a0c30705ce4b7f43516caa0d4af1c6 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 13:52:06 +0100 Subject: [PATCH 05/31] Move predicates to own file --- .../Helpers/KnownReference.Predicates.cs | 73 +++++++++++++++++++ .../Helpers/KnownReference.cs | 50 +------------ 2 files changed, 74 insertions(+), 49 deletions(-) create mode 100644 analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs new file mode 100644 index 00000000000..1b4b1dc85aa --- /dev/null +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -0,0 +1,73 @@ +/* + * 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.Helpers; + +public sealed partial class KnownReference +{ + internal static Func NameIs(string name) => + new(x => x.Name.Equals(name)); + + internal static Func StartsWith(string name) => + new(x => x.Name.StartsWith(name)); + + internal static Func EndsWith(string name) => + new(x => x.Name.EndsWith(name)); + + internal static Func Contains(string name) => + new(x => x.Name.Contains(name)); + + internal static Func VersionLowerThen(string version) => + VersionLowerThen(Version.Parse(version)); + + internal static Func VersionLowerThen(Version version) => + new(x => x.Version < version); + + internal static Func VersionGreaterOrEqual(string version) => + VersionGreaterOrEqual(Version.Parse(version)); + + internal static Func VersionGreaterOrEqual(Version version) => + new(x => x.Version >= version); + + internal static Func VersionBetween(string from, string to) => + VersionBetween(Version.Parse(from), Version.Parse(to)); + + internal static Func VersionBetween(Version from, Version to) => + new(x => x.Version >= from && x.Version <= to); + + internal static Func OptionalPublicKeyTokenIs(string key) => + new(x => !x.HasPublicKey || PublicKeyEqualHex(x, key)); + + internal static Func PublicKeyTokenIs(string key) => + new(x => x.HasPublicKey && PublicKeyEqualHex(x, key)); + + private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexString) + { + var normalizedHexString = hexString.Replace("-", string.Empty); + return ArraysEqual(identity.PublicKeyToken.ToArray(), normalizedHexString) || ArraysEqual(identity.PublicKey.ToArray(), normalizedHexString); + + static bool ArraysEqual(byte[] key, string hexString) => + BitConverter.ToString(key).Replace("-", string.Empty).Equals(hexString, StringComparison.OrdinalIgnoreCase); + } + + internal static Func, bool> Any(Func predicate) => + new(identities => identities.Any(predicate)); + +} diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index be509b40a1d..61f2428c11a 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -20,7 +20,7 @@ namespace SonarAnalyzer.Helpers { - public sealed class KnownReference + public sealed partial class KnownReference { private readonly Func, bool> predicate; @@ -35,54 +35,6 @@ internal KnownReference(Func, bool> predicate) public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen("2.0")))); - internal static Func NameIs(string name) => - new(x => x.Name.Equals(name)); - - internal static Func StartsWith(string name) => - new(x => x.Name.StartsWith(name)); - - internal static Func EndsWith(string name) => - new(x => x.Name.EndsWith(name)); - - internal static Func Contains(string name) => - new(x => x.Name.Contains(name)); - - internal static Func VersionLowerThen(string version) => - VersionLowerThen(Version.Parse(version)); - - internal static Func VersionLowerThen(Version version) => - new(x => x.Version < version); - - internal static Func VersionGreaterOrEqual(string version) => - VersionGreaterOrEqual(Version.Parse(version)); - - internal static Func VersionGreaterOrEqual(Version version) => - new(x => x.Version >= version); - - internal static Func VersionBetween(string from, string to) => - VersionBetween(Version.Parse(from), Version.Parse(to)); - - internal static Func VersionBetween(Version from, Version to) => - new(x => x.Version >= from && x.Version <= to); - - internal static Func OptionalPublicKeyTokenIs(string key) => - new(x => !x.HasPublicKey || PublicKeyEqualHex(x, key)); - - internal static Func PublicKeyTokenIs(string key) => - new(x => x.HasPublicKey && PublicKeyEqualHex(x, key)); - - private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexString) - { - var normalizedHexString = hexString.Replace("-", string.Empty); - return ArraysEqual(identity.PublicKeyToken.ToArray(), normalizedHexString) || ArraysEqual(identity.PublicKey.ToArray(), normalizedHexString); - - static bool ArraysEqual(byte[] key, string hexString) => - BitConverter.ToString(key).Replace("-", string.Empty).Equals(hexString, StringComparison.OrdinalIgnoreCase); - } - - internal static Func, bool> Any(Func predicate) => - new(identities => identities.Any(predicate)); - public bool IsReferenced(Compilation compilation) => predicate(compilation.ReferencedAssemblyNames); } From d3842bda6f46dc996ace8b74511d734081220069 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 13:52:33 +0100 Subject: [PATCH 06/31] Rename to IsReferencedBy --- .../Helpers/CompilationExtensions.cs | 2 +- .../Helpers/KnownReference.cs | 2 +- .../Extensions/KnownReferenceTests.cs | 32 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs index aa0c1aef20d..32caa60caa7 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs @@ -33,6 +33,6 @@ public static class CompilationExtensions compilation.ObjectType.ContainingAssembly.Name == "mscorlib"; public static bool References(this Compilation compilation, KnownReference reference) - => reference.IsReferenced(compilation); + => reference.IsReferencedBy(compilation); } } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index 61f2428c11a..ee5d0e21b54 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -35,7 +35,7 @@ internal KnownReference(Func, bool> predicate) public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen("2.0")))); - public bool IsReferenced(Compilation compilation) => + public bool IsReferencedBy(Compilation compilation) => predicate(compilation.ReferencedAssemblyNames); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index 67ea11c115d..aa70de085b0 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -42,7 +42,7 @@ public void NameIs_Test(string name, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -60,7 +60,7 @@ public void NameContains_Test(string name, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -78,7 +78,7 @@ public void NameStartsWith_Test(string name, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -96,7 +96,7 @@ public void NameEndsWith_Test(string name, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -112,9 +112,9 @@ public void Version_GreaterOrEqual_2_0(string version, bool expected) var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); var sut = new KnownReference(VersionGreaterOrEqual(new Version(2, 0))); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownReference(VersionGreaterOrEqual("2.0")); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -130,9 +130,9 @@ public void Version_LowerThen_2_0(string version, bool expected) var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); var sut = new KnownReference(VersionLowerThen(new Version(2, 0))); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownReference(VersionLowerThen("2.0")); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -152,9 +152,9 @@ public void Version_Between_2_0_and_3_5(string version, bool expected) var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); var sut = new KnownReference(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownReference(VersionBetween("2.0.0.0", "3.5.0.0")); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -178,9 +178,9 @@ public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expect 0xec, 0xe2, 0x56, 0x35, 0x7b, 0xa7, 0x35, 0xe6, 0x7d, 0xc6), hasPublicKey: true); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); var sut = new KnownReference(PublicKeyTokenIs(publicKeyToken)); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownReference(OptionalPublicKeyTokenIs(publicKeyToken)); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [TestMethod] @@ -190,7 +190,7 @@ public void PublicKeyTokenIs_FailsWhenKeyIsMissing() var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); var sut = new KnownReference(PublicKeyTokenIs("c5b62af9de6d7244")); - sut.IsReferenced(compilation.Object).Should().BeFalse(); + sut.IsReferencedBy(compilation.Object).Should().BeFalse(); } [TestMethod] @@ -200,7 +200,7 @@ public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); var sut = new KnownReference(OptionalPublicKeyTokenIs("c5b62af9de6d7244")); - sut.IsReferenced(compilation.Object).Should().BeTrue(); + sut.IsReferencedBy(compilation.Object).Should().BeTrue(); } [DataTestMethod] @@ -217,7 +217,7 @@ public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name, new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [DataTestMethod] @@ -235,7 +235,7 @@ public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expect var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - sut.IsReferenced(compilation.Object).Should().Be(expected); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); } [TestMethod] From 92deb061764e05ecbf4861a4eeb22d3cae9db9d0 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 13:55:13 +0100 Subject: [PATCH 07/31] Clean-up --- .../Helpers/KnownReference.Predicates.cs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs index 1b4b1dc85aa..1e28cdbd076 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -23,40 +23,40 @@ namespace SonarAnalyzer.Helpers; public sealed partial class KnownReference { internal static Func NameIs(string name) => - new(x => x.Name.Equals(name)); + x => x.Name.Equals(name); internal static Func StartsWith(string name) => - new(x => x.Name.StartsWith(name)); + x => x.Name.StartsWith(name); internal static Func EndsWith(string name) => - new(x => x.Name.EndsWith(name)); + x => x.Name.EndsWith(name); internal static Func Contains(string name) => - new(x => x.Name.Contains(name)); + x => x.Name.Contains(name); internal static Func VersionLowerThen(string version) => VersionLowerThen(Version.Parse(version)); internal static Func VersionLowerThen(Version version) => - new(x => x.Version < version); + x => x.Version < version; internal static Func VersionGreaterOrEqual(string version) => VersionGreaterOrEqual(Version.Parse(version)); internal static Func VersionGreaterOrEqual(Version version) => - new(x => x.Version >= version); + x => x.Version >= version; internal static Func VersionBetween(string from, string to) => VersionBetween(Version.Parse(from), Version.Parse(to)); internal static Func VersionBetween(Version from, Version to) => - new(x => x.Version >= from && x.Version <= to); + x => x.Version >= from && x.Version <= to; internal static Func OptionalPublicKeyTokenIs(string key) => - new(x => !x.HasPublicKey || PublicKeyEqualHex(x, key)); + x => !x.HasPublicKey || PublicKeyEqualHex(x, key); internal static Func PublicKeyTokenIs(string key) => - new(x => x.HasPublicKey && PublicKeyEqualHex(x, key)); + x => x.HasPublicKey && PublicKeyEqualHex(x, key); private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexString) { @@ -68,6 +68,5 @@ private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexStrin } internal static Func, bool> Any(Func predicate) => - new(identities => identities.Any(predicate)); - + identities => identities.Any(predicate); } From 8422676952a4a4fef1f69198500e1fc187991607 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 14:03:02 +0100 Subject: [PATCH 08/31] Add argument null checks --- .../Helpers/KnownReference.Predicates.cs | 4 +++- .../SonarAnalyzer.Common/Helpers/KnownReference.cs | 2 +- .../Extensions/KnownReferenceTests.cs | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs index 1e28cdbd076..8a70b215f4f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -68,5 +68,7 @@ private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexStrin } internal static Func, bool> Any(Func predicate) => - identities => identities.Any(predicate); + predicate is null + ? throw new ArgumentNullException(nameof(predicate)) + : (identities => identities.Any(predicate)); } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index ee5d0e21b54..929da70756f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -30,7 +30,7 @@ internal KnownReference(Func predicate) : this(Any(predi internal KnownReference(Func, bool> predicate) { - this.predicate = predicate; + this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); } public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen("2.0")))); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index aa70de085b0..68198e8f1a3 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -27,6 +27,20 @@ namespace SonarAnalyzer.UnitTest; [TestClass] public class KnownReferenceTests { + [TestMethod] + public void KnownReference_ThrowsWhenPredicateNull_1() + { + var sut = () => new KnownReference(default(Func)); + sut.Should().Throw(); + } + + [TestMethod] + public void KnownReference_ThrowsWhenPredicateNull_2() + { + var sut = () => new KnownReference(default(Func, bool>)); + sut.Should().Throw(); + } + [DataTestMethod] [DataRow("Test", true)] [DataRow("test", false)] From ec96228a8d00cb859bad0ecb4f0b9d542ca1b094 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 7 Feb 2023 14:05:55 +0100 Subject: [PATCH 09/31] Clean-up --- .../SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs index 8a70b215f4f..77119fc88de 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -70,5 +70,5 @@ private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexStrin internal static Func, bool> Any(Func predicate) => predicate is null ? throw new ArgumentNullException(nameof(predicate)) - : (identities => identities.Any(predicate)); + : identities => identities.Any(predicate); } From ab95c9b55afe54ceb68a126496a4ff11cfd5e61a Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 8 Feb 2023 15:58:08 +0100 Subject: [PATCH 10/31] Remove Or and replace with params --- .../Helpers/KnownReference.cs | 22 +++++++++++++++---- .../Helpers/KnownReferenceExtensions.cs | 3 --- .../Extensions/KnownReferenceTests.cs | 13 ++++++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index 929da70756f..fe54b0b43a8 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -24,16 +24,30 @@ public sealed partial class KnownReference { private readonly Func, bool> predicate; - internal KnownReference(Func predicate) : this(Any(predicate)) + internal KnownReference(Func predicate, params Func[] or) + : this(Any(predicate, or)) { } - internal KnownReference(Func, bool> predicate) - { + internal KnownReference(Func, bool> predicate) => this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + + private static Func, bool> Any(Func predicate, Func[] or) + { + if (predicate is null) + { + throw new ArgumentNullException(nameof(predicate)); + } + if (or.Any(x => x is null)) + { + throw new ArgumentException("predicate should not be null", nameof(or)); + } + return Any(identitiy => predicate(identitiy) || or.Any(x => x(identitiy))); } - public static KnownReference XUnit_Assert { get; } = new(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen("2.0")))); + public static KnownReference XUnit_Assert { get; } = new( + NameIs("xunit.assert"), + NameIs("xunit").And(VersionLowerThen("2.0"))); public bool IsReferencedBy(Compilation compilation) => predicate(compilation.ReferencedAssemblyNames); diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs index 23873c6c031..7b061890fcc 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs @@ -24,7 +24,4 @@ internal static class KnownReferenceExtensions { internal static Func And(this Func @this, Func predicate) => identity => @this(identity) && predicate(identity); - - internal static Func Or(this Func @this, Func predicate) - => identity => @this(identity) || predicate(identity); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index 68198e8f1a3..dd17c5f995f 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -31,14 +31,21 @@ public class KnownReferenceTests public void KnownReference_ThrowsWhenPredicateNull_1() { var sut = () => new KnownReference(default(Func)); - sut.Should().Throw(); + sut.Should().Throw().Which.ParamName.Should().Be("predicate"); } [TestMethod] public void KnownReference_ThrowsWhenPredicateNull_2() { var sut = () => new KnownReference(default(Func, bool>)); - sut.Should().Throw(); + sut.Should().Throw().Which.ParamName.Should().Be("predicate"); + } + + [TestMethod] + public void KnownReference_ThrowsWhenPredicateNull_params() + { + var sut = () => new KnownReference(_ => true, null, x => true); + sut.Should().Throw().Which.ParamName.Should().Be("or"); } [DataTestMethod] @@ -245,7 +252,7 @@ public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string [DataRow("SomethingStart", false)] public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expected) { - var sut = new KnownReference(StartsWith("Start").Or(EndsWith("End"))); + var sut = new KnownReference(StartsWith("Start"), EndsWith("End")); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); From 4caa68999c61daddb0646048ccba69ea0d80b1d1 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 8 Feb 2023 18:28:07 +0100 Subject: [PATCH 11/31] argument naming --- .../SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index dd17c5f995f..2b56b842b53 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -44,7 +44,7 @@ public void KnownReference_ThrowsWhenPredicateNull_2() [TestMethod] public void KnownReference_ThrowsWhenPredicateNull_params() { - var sut = () => new KnownReference(_ => true, null, x => true); + var sut = () => new KnownReference(_ => true, null, _ => true); sut.Should().Throw().Which.ParamName.Should().Be("or"); } From af2cdc8249cf9cdda955fc2b2064b7fabc8f74c4 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 8 Feb 2023 18:28:24 +0100 Subject: [PATCH 12/31] test Naming --- .../SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index 2b56b842b53..eaceda74c48 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -42,7 +42,7 @@ public void KnownReference_ThrowsWhenPredicateNull_2() } [TestMethod] - public void KnownReference_ThrowsWhenPredicateNull_params() + public void KnownReference_ThrowsWhenPredicateNull_Params() { var sut = () => new KnownReference(_ => true, null, _ => true); sut.Should().Throw().Which.ParamName.Should().Be("or"); From c4ae3a45cd11354e81aa146b3805397e035959d1 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 9 Feb 2023 14:24:40 +0100 Subject: [PATCH 13/31] Move extension methods to the right file ine the right folder --- .../Extensions/CompilationExtensions.cs | 13 +++++++ .../Helpers/CompilationExtensions.cs | 38 ------------------- 2 files changed, 13 insertions(+), 38 deletions(-) delete mode 100644 analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs diff --git a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs index e0c5953eebd..b6b93487200 100644 --- a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs @@ -24,4 +24,17 @@ internal static class CompilationExtensions { public static INamedTypeSymbol GetTypeByMetadataName(this Compilation compilation, KnownType knownType) => compilation.GetTypeByMetadataName(knownType.FullName); + + public static IMethodSymbol GetTypeMethod(this Compilation compilation, SpecialType type, string methodName) => + (IMethodSymbol)compilation.GetSpecialType(type) + .GetMembers(methodName) + .SingleOrDefault(); + + public static bool IsNetFrameworkTarget(this Compilation compilation) => + // There's no direct way of checking compilation target framework yet (09/2020). + // See https://github.com/dotnet/roslyn/issues/3798 + compilation.ObjectType.ContainingAssembly.Name == "mscorlib"; + + public static bool References(this Compilation compilation, KnownReference reference) + => reference.IsReferencedBy(compilation); } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs deleted file mode 100644 index 32caa60caa7..00000000000 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/CompilationExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.Helpers -{ - public static class CompilationExtensions - { - public static IMethodSymbol GetTypeMethod(this Compilation compilation, SpecialType type, string methodName) => - (IMethodSymbol)compilation.GetSpecialType(type) - .GetMembers(methodName) - .SingleOrDefault(); - - public static bool IsNetFrameworkTarget(this Compilation compilation) => - // There's no direct way of checking compilation target framework yet (09/2020). - // See https://github.com/dotnet/roslyn/issues/3798 - compilation.ObjectType.ContainingAssembly.Name == "mscorlib"; - - public static bool References(this Compilation compilation, KnownReference reference) - => reference.IsReferencedBy(compilation); - } -} From 657c32ed845e5b4ea9459609ccfd9b62fa37e05f Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 9 Feb 2023 14:25:04 +0100 Subject: [PATCH 14/31] Add suing after move --- .../Helpers/KnownReference.Predicates.cs | 8 +++++--- .../Extensions/KnownReferenceTests.cs | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs index 77119fc88de..3a34f9209bd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -22,14 +22,16 @@ namespace SonarAnalyzer.Helpers; public sealed partial class KnownReference { + private const StringComparison AssemblyNameComparission = StringComparison.OrdinalIgnoreCase; + internal static Func NameIs(string name) => - x => x.Name.Equals(name); + x => x.Name.Equals(name, AssemblyNameComparission); internal static Func StartsWith(string name) => - x => x.Name.StartsWith(name); + x => x.Name.StartsWith(name, AssemblyNameComparission); internal static Func EndsWith(string name) => - x => x.Name.EndsWith(name); + x => x.Name.EndsWith(name, AssemblyNameComparission); internal static Func Contains(string name) => x => x.Name.Contains(name); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index eaceda74c48..de35dbfd741 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -20,6 +20,7 @@ using Moq; using SonarAnalyzer.Common; +using SonarAnalyzer.Extensions; using static SonarAnalyzer.Helpers.KnownReference; namespace SonarAnalyzer.UnitTest; From 9bef92a1fe945fd6bfa1761c51385d72132d5442 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 08:42:26 +0100 Subject: [PATCH 15/31] Use IndexOf for "Contains" --- .../Helpers/KnownReference.Predicates.cs | 76 ++++++++++--------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs index 3a34f9209bd..02fd371e47f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -18,59 +18,65 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.ComponentModel; + namespace SonarAnalyzer.Helpers; public sealed partial class KnownReference { private const StringComparison AssemblyNameComparission = StringComparison.OrdinalIgnoreCase; - internal static Func NameIs(string name) => - x => x.Name.Equals(name, AssemblyNameComparission); + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class Predicates + { + internal static Func NameIs(string name) => + x => x.Name.Equals(name, AssemblyNameComparission); - internal static Func StartsWith(string name) => - x => x.Name.StartsWith(name, AssemblyNameComparission); + internal static Func StartsWith(string name) => + x => x.Name.StartsWith(name, AssemblyNameComparission); - internal static Func EndsWith(string name) => - x => x.Name.EndsWith(name, AssemblyNameComparission); + internal static Func EndsWith(string name) => + x => x.Name.EndsWith(name, AssemblyNameComparission); - internal static Func Contains(string name) => - x => x.Name.Contains(name); + internal static Func Contains(string name) => + x => x.Name.IndexOf(name, 0, AssemblyNameComparission) >= 0; - internal static Func VersionLowerThen(string version) => - VersionLowerThen(Version.Parse(version)); + internal static Func VersionLowerThen(string version) => + VersionLowerThen(Version.Parse(version)); - internal static Func VersionLowerThen(Version version) => - x => x.Version < version; + internal static Func VersionLowerThen(Version version) => + x => x.Version < version; - internal static Func VersionGreaterOrEqual(string version) => - VersionGreaterOrEqual(Version.Parse(version)); + internal static Func VersionGreaterOrEqual(string version) => + VersionGreaterOrEqual(Version.Parse(version)); - internal static Func VersionGreaterOrEqual(Version version) => - x => x.Version >= version; + internal static Func VersionGreaterOrEqual(Version version) => + x => x.Version >= version; - internal static Func VersionBetween(string from, string to) => - VersionBetween(Version.Parse(from), Version.Parse(to)); + internal static Func VersionBetween(string from, string to) => + VersionBetween(Version.Parse(from), Version.Parse(to)); - internal static Func VersionBetween(Version from, Version to) => - x => x.Version >= from && x.Version <= to; + internal static Func VersionBetween(Version from, Version to) => + x => x.Version >= from && x.Version <= to; - internal static Func OptionalPublicKeyTokenIs(string key) => - x => !x.HasPublicKey || PublicKeyEqualHex(x, key); + internal static Func OptionalPublicKeyTokenIs(string key) => + x => !x.HasPublicKey || PublicKeyEqualHex(x, key); - internal static Func PublicKeyTokenIs(string key) => - x => x.HasPublicKey && PublicKeyEqualHex(x, key); + internal static Func PublicKeyTokenIs(string key) => + x => x.HasPublicKey && PublicKeyEqualHex(x, key); - private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexString) - { - var normalizedHexString = hexString.Replace("-", string.Empty); - return ArraysEqual(identity.PublicKeyToken.ToArray(), normalizedHexString) || ArraysEqual(identity.PublicKey.ToArray(), normalizedHexString); + private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexString) + { + var normalizedHexString = hexString.Replace("-", string.Empty); + return ArraysEqual(identity.PublicKeyToken.ToArray(), normalizedHexString) || ArraysEqual(identity.PublicKey.ToArray(), normalizedHexString); - static bool ArraysEqual(byte[] key, string hexString) => - BitConverter.ToString(key).Replace("-", string.Empty).Equals(hexString, StringComparison.OrdinalIgnoreCase); - } + static bool ArraysEqual(byte[] key, string hexString) => + BitConverter.ToString(key).Replace("-", string.Empty).Equals(hexString, StringComparison.OrdinalIgnoreCase); + } - internal static Func, bool> Any(Func predicate) => - predicate is null - ? throw new ArgumentNullException(nameof(predicate)) - : identities => identities.Any(predicate); + internal static Func, bool> Any(Func predicate) => + predicate is null + ? throw new ArgumentNullException(nameof(predicate)) + : identities => identities.Any(predicate); + } } From 9450d62def0546a69d2e04014124e987c175fe0f Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 08:43:25 +0100 Subject: [PATCH 16/31] Adopt tests for string comparison --- .../Extensions/KnownReferenceTests.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs index de35dbfd741..9d50ed2e59b 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs @@ -22,6 +22,7 @@ using SonarAnalyzer.Common; using SonarAnalyzer.Extensions; using static SonarAnalyzer.Helpers.KnownReference; +using static SonarAnalyzer.Helpers.KnownReference.Predicates; namespace SonarAnalyzer.UnitTest; @@ -46,13 +47,13 @@ public void KnownReference_ThrowsWhenPredicateNull_2() public void KnownReference_ThrowsWhenPredicateNull_Params() { var sut = () => new KnownReference(_ => true, null, _ => true); - sut.Should().Throw().Which.ParamName.Should().Be("or"); + sut.Should().Throw().Which.ParamName.Should().Be("predicate"); } [DataTestMethod] [DataRow("Test", true)] - [DataRow("test", false)] - [DataRow("TEST", false)] + [DataRow("test", true)] + [DataRow("TEST", true)] [DataRow("MyTest", false)] [DataRow("TestMy", false)] [DataRow("MyTestMy", false)] @@ -69,12 +70,12 @@ public void NameIs_Test(string name, bool expected) [DataTestMethod] [DataRow("Test", true)] - [DataRow("test", false)] - [DataRow("TEST", false)] + [DataRow("test", true)] + [DataRow("TEST", true)] [DataRow("MyTest", true)] [DataRow("TestMy", true)] [DataRow("MyTestMy", true)] - [DataRow("MyTESTMy", false)] + [DataRow("MyTESTMy", true)] [DataRow("Without", false)] public void NameContains_Test(string name, bool expected) { @@ -87,8 +88,8 @@ public void NameContains_Test(string name, bool expected) [DataTestMethod] [DataRow("Test", true)] - [DataRow("test", false)] - [DataRow("TEST", false)] + [DataRow("test", true)] + [DataRow("TEST", true)] [DataRow("MyTest", false)] [DataRow("TestMy", true)] [DataRow("MyTestMy", false)] @@ -105,8 +106,8 @@ public void NameStartsWith_Test(string name, bool expected) [DataTestMethod] [DataRow("Test", true)] - [DataRow("test", false)] - [DataRow("TEST", false)] + [DataRow("test", true)] + [DataRow("TEST", true)] [DataRow("MyTest", true)] [DataRow("TestMy", false)] [DataRow("MyTestMy", false)] From 886b451b6d105e1c255591aecdc4e5e86a6ef2a0 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 08:43:43 +0100 Subject: [PATCH 17/31] Use nested class --- .../Helpers/KnownReference.cs | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index fe54b0b43a8..19f25470a7a 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -18,37 +18,28 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using static SonarAnalyzer.Helpers.KnownReference.Predicates; + namespace SonarAnalyzer.Helpers { public sealed partial class KnownReference { private readonly Func, bool> predicate; + public static KnownReference XUnit_Assert { get; } = new( + NameIs("xunit.assert"), + NameIs("xunit").And(VersionLowerThen("2.0"))); + internal KnownReference(Func predicate, params Func[] or) - : this(Any(predicate, or)) + : this(predicate is null || or.Any(x => x is null) + ? throw new ArgumentNullException(nameof(predicate)) + : Any(identitiy => predicate(identitiy) || or.Any(x => x(identitiy)))) { } internal KnownReference(Func, bool> predicate) => this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); - private static Func, bool> Any(Func predicate, Func[] or) - { - if (predicate is null) - { - throw new ArgumentNullException(nameof(predicate)); - } - if (or.Any(x => x is null)) - { - throw new ArgumentException("predicate should not be null", nameof(or)); - } - return Any(identitiy => predicate(identitiy) || or.Any(x => x(identitiy))); - } - - public static KnownReference XUnit_Assert { get; } = new( - NameIs("xunit.assert"), - NameIs("xunit").And(VersionLowerThen("2.0"))); - public bool IsReferencedBy(Compilation compilation) => predicate(compilation.ReferencedAssemblyNames); } From 98364c7ec70fb6900ba55e0301c1dde96355f438 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 08:49:20 +0100 Subject: [PATCH 18/31] Move to extensions folder --- .../{Helpers => Extensions}/KnownReferenceExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename analyzers/src/SonarAnalyzer.Common/{Helpers => Extensions}/KnownReferenceExtensions.cs (96%) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/KnownReferenceExtensions.cs similarity index 96% rename from analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs rename to analyzers/src/SonarAnalyzer.Common/Extensions/KnownReferenceExtensions.cs index 7b061890fcc..b5c1ebf7560 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReferenceExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/KnownReferenceExtensions.cs @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -namespace SonarAnalyzer.Helpers; +namespace SonarAnalyzer.Extensions; internal static class KnownReferenceExtensions { From abed89f74b1f3d9a1cac62b62d825d27caf614c4 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 11:17:09 +0100 Subject: [PATCH 19/31] Remove "Any" extension method --- .../Helpers/KnownReference.Predicates.cs | 5 ----- analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs index 02fd371e47f..361a35d1b8e 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs @@ -73,10 +73,5 @@ private static bool PublicKeyEqualHex(AssemblyIdentity identity, string hexStrin static bool ArraysEqual(byte[] key, string hexString) => BitConverter.ToString(key).Replace("-", string.Empty).Equals(hexString, StringComparison.OrdinalIgnoreCase); } - - internal static Func, bool> Any(Func predicate) => - predicate is null - ? throw new ArgumentNullException(nameof(predicate)) - : identities => identities.Any(predicate); } } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index 19f25470a7a..0cef636d34a 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -33,7 +33,7 @@ public sealed partial class KnownReference internal KnownReference(Func predicate, params Func[] or) : this(predicate is null || or.Any(x => x is null) ? throw new ArgumentNullException(nameof(predicate)) - : Any(identitiy => predicate(identitiy) || or.Any(x => x(identitiy)))) + : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(x => x(identitiy)))) { } From d6193462169e6711f5a7897d80f0de6d013445f5 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 11:18:06 +0100 Subject: [PATCH 20/31] Rename parameter --- analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index 0cef636d34a..536e9d4f759 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -33,7 +33,7 @@ public sealed partial class KnownReference internal KnownReference(Func predicate, params Func[] or) : this(predicate is null || or.Any(x => x is null) ? throw new ArgumentNullException(nameof(predicate)) - : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(x => x(identitiy)))) + : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(orPredicate => orPredicate(identitiy)))) { } From 6d341b143e50f18d0457b23f1721f1e5f5b58552 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 10 Feb 2023 11:19:17 +0100 Subject: [PATCH 21/31] Exception message --- analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs index 536e9d4f759..25aa4199cf9 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs @@ -32,7 +32,7 @@ public sealed partial class KnownReference internal KnownReference(Func predicate, params Func[] or) : this(predicate is null || or.Any(x => x is null) - ? throw new ArgumentNullException(nameof(predicate)) + ? throw new ArgumentNullException(nameof(predicate), "All predicates must be non-null.") : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(orPredicate => orPredicate(identitiy)))) { } From 078a782d221893a897104cb68025e4849de0d9d3 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 11:54:20 +0100 Subject: [PATCH 22/31] Rename KnownReference to KnownAssembly --- .../Extensions/CompilationExtensions.cs | 4 +- ...tensions.cs => KnownAssemblyExtensions.cs} | 2 +- ...dicates.cs => KnownAssembly.Predicates.cs} | 2 +- .../{KnownReference.cs => KnownAssembly.cs} | 10 ++--- ...ReferenceTests.cs => KnownAssemblyTest.cs} | 44 +++++++++---------- 5 files changed, 31 insertions(+), 31 deletions(-) rename analyzers/src/SonarAnalyzer.Common/Extensions/{KnownReferenceExtensions.cs => KnownAssemblyExtensions.cs} (95%) rename analyzers/src/SonarAnalyzer.Common/Helpers/{KnownReference.Predicates.cs => KnownAssembly.Predicates.cs} (98%) rename analyzers/src/SonarAnalyzer.Common/Helpers/{KnownReference.cs => KnownAssembly.cs} (81%) rename analyzers/tests/SonarAnalyzer.UnitTest/Extensions/{KnownReferenceTests.cs => KnownAssemblyTest.cs} (88%) diff --git a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs index b6b93487200..0e43eab7ede 100644 --- a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs @@ -35,6 +35,6 @@ internal static class CompilationExtensions // See https://github.com/dotnet/roslyn/issues/3798 compilation.ObjectType.ContainingAssembly.Name == "mscorlib"; - public static bool References(this Compilation compilation, KnownReference reference) - => reference.IsReferencedBy(compilation); + public static bool References(this Compilation compilation, KnownAssembly assembly) + => assembly.IsReferencedBy(compilation); } diff --git a/analyzers/src/SonarAnalyzer.Common/Extensions/KnownReferenceExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs similarity index 95% rename from analyzers/src/SonarAnalyzer.Common/Extensions/KnownReferenceExtensions.cs rename to analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs index b5c1ebf7560..97050f9db03 100644 --- a/analyzers/src/SonarAnalyzer.Common/Extensions/KnownReferenceExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs @@ -20,7 +20,7 @@ namespace SonarAnalyzer.Extensions; -internal static class KnownReferenceExtensions +internal static class KnownAssemblyExtensions { internal static Func And(this Func @this, Func predicate) => identity => @this(identity) && predicate(identity); diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs similarity index 98% rename from analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs rename to analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs index 361a35d1b8e..dac0fad717b 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs @@ -22,7 +22,7 @@ namespace SonarAnalyzer.Helpers; -public sealed partial class KnownReference +public sealed partial class KnownAssembly { private const StringComparison AssemblyNameComparission = StringComparison.OrdinalIgnoreCase; diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs similarity index 81% rename from analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs rename to analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs index 25aa4199cf9..daba977fcfd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownReference.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs @@ -18,26 +18,26 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using static SonarAnalyzer.Helpers.KnownReference.Predicates; +using static SonarAnalyzer.Helpers.KnownAssembly.Predicates; namespace SonarAnalyzer.Helpers { - public sealed partial class KnownReference + public sealed partial class KnownAssembly { private readonly Func, bool> predicate; - public static KnownReference XUnit_Assert { get; } = new( + public static KnownAssembly XUnit_Assert { get; } = new( NameIs("xunit.assert"), NameIs("xunit").And(VersionLowerThen("2.0"))); - internal KnownReference(Func predicate, params Func[] or) + internal KnownAssembly(Func predicate, params Func[] or) : this(predicate is null || or.Any(x => x is null) ? throw new ArgumentNullException(nameof(predicate), "All predicates must be non-null.") : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(orPredicate => orPredicate(identitiy)))) { } - internal KnownReference(Func, bool> predicate) => + internal KnownAssembly(Func, bool> predicate) => this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); public bool IsReferencedBy(Compilation compilation) => diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownAssemblyTest.cs similarity index 88% rename from analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs rename to analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownAssemblyTest.cs index 9d50ed2e59b..ebfc2547a61 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownReferenceTests.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownAssemblyTest.cs @@ -21,32 +21,32 @@ using Moq; using SonarAnalyzer.Common; using SonarAnalyzer.Extensions; -using static SonarAnalyzer.Helpers.KnownReference; -using static SonarAnalyzer.Helpers.KnownReference.Predicates; +using static SonarAnalyzer.Helpers.KnownAssembly; +using static SonarAnalyzer.Helpers.KnownAssembly.Predicates; namespace SonarAnalyzer.UnitTest; [TestClass] -public class KnownReferenceTests +public class KnownAssemblyTest { [TestMethod] public void KnownReference_ThrowsWhenPredicateNull_1() { - var sut = () => new KnownReference(default(Func)); + var sut = () => new KnownAssembly(default(Func)); sut.Should().Throw().Which.ParamName.Should().Be("predicate"); } [TestMethod] public void KnownReference_ThrowsWhenPredicateNull_2() { - var sut = () => new KnownReference(default(Func, bool>)); + var sut = () => new KnownAssembly(default(Func, bool>)); sut.Should().Throw().Which.ParamName.Should().Be("predicate"); } [TestMethod] public void KnownReference_ThrowsWhenPredicateNull_Params() { - var sut = () => new KnownReference(_ => true, null, _ => true); + var sut = () => new KnownAssembly(_ => true, null, _ => true); sut.Should().Throw().Which.ParamName.Should().Be("predicate"); } @@ -61,7 +61,7 @@ public void KnownReference_ThrowsWhenPredicateNull_Params() [DataRow("Without", false)] public void NameIs_Test(string name, bool expected) { - var sut = new KnownReference(NameIs("Test")); + var sut = new KnownAssembly(NameIs("Test")); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); @@ -79,7 +79,7 @@ public void NameIs_Test(string name, bool expected) [DataRow("Without", false)] public void NameContains_Test(string name, bool expected) { - var sut = new KnownReference(Contains("Test")); + var sut = new KnownAssembly(Contains("Test")); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); @@ -97,7 +97,7 @@ public void NameContains_Test(string name, bool expected) [DataRow("Without", false)] public void NameStartsWith_Test(string name, bool expected) { - var sut = new KnownReference(StartsWith("Test")); + var sut = new KnownAssembly(StartsWith("Test")); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); @@ -115,7 +115,7 @@ public void NameStartsWith_Test(string name, bool expected) [DataRow("Without", false)] public void NameEndsWith_Test(string name, bool expected) { - var sut = new KnownReference(EndsWith("Test")); + var sut = new KnownAssembly(EndsWith("Test")); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); @@ -134,9 +134,9 @@ public void Version_GreaterOrEqual_2_0(string version, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - var sut = new KnownReference(VersionGreaterOrEqual(new Version(2, 0))); + var sut = new KnownAssembly(VersionGreaterOrEqual(new Version(2, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); - sut = new KnownReference(VersionGreaterOrEqual("2.0")); + sut = new KnownAssembly(VersionGreaterOrEqual("2.0")); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -152,9 +152,9 @@ public void Version_LowerThen_2_0(string version, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - var sut = new KnownReference(VersionLowerThen(new Version(2, 0))); + var sut = new KnownAssembly(VersionLowerThen(new Version(2, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); - sut = new KnownReference(VersionLowerThen("2.0")); + sut = new KnownAssembly(VersionLowerThen("2.0")); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -174,9 +174,9 @@ public void Version_Between_2_0_and_3_5(string version, bool expected) var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - var sut = new KnownReference(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); + var sut = new KnownAssembly(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); - sut = new KnownReference(VersionBetween("2.0.0.0", "3.5.0.0")); + sut = new KnownAssembly(VersionBetween("2.0.0.0", "3.5.0.0")); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -200,9 +200,9 @@ public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expect 0x98, 0x71, 0x04, 0x3b, 0x72, 0x41, 0xac, 0x4a, 0xb9, 0xf6, 0xb3, 0x4f, 0x18, 0x3d, 0xb7, 0x16, 0x08, 0x2c, 0xd5, 0x7c, 0x1f, 0xf6, 0x48, 0x13, 0x5b, 0xec, 0xe2, 0x56, 0x35, 0x7b, 0xa7, 0x35, 0xe6, 0x7d, 0xc6), hasPublicKey: true); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - var sut = new KnownReference(PublicKeyTokenIs(publicKeyToken)); + var sut = new KnownAssembly(PublicKeyTokenIs(publicKeyToken)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); - sut = new KnownReference(OptionalPublicKeyTokenIs(publicKeyToken)); + sut = new KnownAssembly(OptionalPublicKeyTokenIs(publicKeyToken)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -212,7 +212,7 @@ public void PublicKeyTokenIs_FailsWhenKeyIsMissing() var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - var sut = new KnownReference(PublicKeyTokenIs("c5b62af9de6d7244")); + var sut = new KnownAssembly(PublicKeyTokenIs("c5b62af9de6d7244")); sut.IsReferencedBy(compilation.Object).Should().BeFalse(); } @@ -222,7 +222,7 @@ public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); - var sut = new KnownReference(OptionalPublicKeyTokenIs("c5b62af9de6d7244")); + var sut = new KnownAssembly(OptionalPublicKeyTokenIs("c5b62af9de6d7244")); sut.IsReferencedBy(compilation.Object).Should().BeTrue(); } @@ -236,7 +236,7 @@ public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() [DataRow("Test", "10.0.0.0", false)] public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string name, string version, bool expected) { - var sut = new KnownReference(StartsWith("Test").And(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0)))); + var sut = new KnownAssembly(StartsWith("Test").And(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0)))); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name, new Version(version)); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); @@ -254,7 +254,7 @@ public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string [DataRow("SomethingStart", false)] public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expected) { - var sut = new KnownReference(StartsWith("Start"), EndsWith("End")); + var sut = new KnownAssembly(StartsWith("Start"), EndsWith("End")); var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); From f9bbb8a65d537fccffad91369f4e0111ee71c346 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:10:56 +0100 Subject: [PATCH 23/31] Rename SpecialTypeMethod --- analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeFromDispose.cs | 2 +- .../SonarAnalyzer.CSharp/Rules/DisposeNotImplementingDispose.cs | 2 +- .../SonarAnalyzer.Common/Extensions/CompilationExtensions.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeFromDispose.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeFromDispose.cs index 2e155c8e37a..fc53f438324 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeFromDispose.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeFromDispose.cs @@ -73,7 +73,7 @@ protected override void Initialize(SonarAnalysisContext context) private static bool IsDisposeMethodCalled(InvocationExpressionSyntax invocation, SemanticModel semanticModel, LanguageVersion languageVersion) => semanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && KnownMethods.IsIDisposableDispose(methodSymbol) - && semanticModel.Compilation.GetTypeMethod(SpecialType.System_IDisposable, DisposeMethodName) is { } disposeMethodSignature + && semanticModel.Compilation.SpecialTypeMethod(SpecialType.System_IDisposable, DisposeMethodName) is { } disposeMethodSignature && (methodSymbol.Equals(methodSymbol.ContainingType.FindImplementationForInterfaceMember(disposeMethodSignature)) || methodSymbol.ContainingType.IsDisposableRefStruct(languageVersion)); diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeNotImplementingDispose.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeNotImplementingDispose.cs index b3cc230d70a..382e249afe6 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeNotImplementingDispose.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/DisposeNotImplementingDispose.cs @@ -43,7 +43,7 @@ public sealed class DisposeNotImplementingDispose : SonarDiagnosticAnalyzer return; } - var disposeMethod = c.Compilation.GetTypeMethod(SpecialType.System_IDisposable, "Dispose"); + var disposeMethod = c.Compilation.SpecialTypeMethod(SpecialType.System_IDisposable, "Dispose"); if (disposeMethod == null) { return; diff --git a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs index 0e43eab7ede..fe11320c015 100644 --- a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs @@ -25,7 +25,7 @@ internal static class CompilationExtensions public static INamedTypeSymbol GetTypeByMetadataName(this Compilation compilation, KnownType knownType) => compilation.GetTypeByMetadataName(knownType.FullName); - public static IMethodSymbol GetTypeMethod(this Compilation compilation, SpecialType type, string methodName) => + public static IMethodSymbol SpecialTypeMethod(this Compilation compilation, SpecialType type, string methodName) => (IMethodSymbol)compilation.GetSpecialType(type) .GetMembers(methodName) .SingleOrDefault(); From 0cd2ee6ce5f1321954bb3bf48b1691c5344f1b96 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:11:23 +0100 Subject: [PATCH 24/31] Formatting --- .../SonarAnalyzer.Common/Extensions/CompilationExtensions.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs index fe11320c015..2288e0481c2 100644 --- a/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/CompilationExtensions.cs @@ -26,9 +26,7 @@ internal static class CompilationExtensions compilation.GetTypeByMetadataName(knownType.FullName); public static IMethodSymbol SpecialTypeMethod(this Compilation compilation, SpecialType type, string methodName) => - (IMethodSymbol)compilation.GetSpecialType(type) - .GetMembers(methodName) - .SingleOrDefault(); + (IMethodSymbol)compilation.GetSpecialType(type).GetMembers(methodName).SingleOrDefault(); public static bool IsNetFrameworkTarget(this Compilation compilation) => // There's no direct way of checking compilation target framework yet (09/2020). From 88e2f904ee2316ef266c90fa234954a0886b8bcf Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:26:04 +0100 Subject: [PATCH 25/31] Move file --- .../{Extensions => Helpers}/KnownAssemblyTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename analyzers/tests/SonarAnalyzer.UnitTest/{Extensions => Helpers}/KnownAssemblyTest.cs (99%) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownAssemblyTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs similarity index 99% rename from analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownAssemblyTest.cs rename to analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs index ebfc2547a61..621efdce555 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Extensions/KnownAssemblyTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs @@ -24,7 +24,7 @@ using static SonarAnalyzer.Helpers.KnownAssembly; using static SonarAnalyzer.Helpers.KnownAssembly.Predicates; -namespace SonarAnalyzer.UnitTest; +namespace SonarAnalyzer.UnitTest.Helpers; [TestClass] public class KnownAssemblyTest From d2794d2f2a28f9a499e536a8c35537c1fbcac68d Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:26:29 +0100 Subject: [PATCH 26/31] File scoped namespace --- .../Helpers/KnownAssembly.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs index daba977fcfd..311017f63df 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs @@ -20,27 +20,26 @@ using static SonarAnalyzer.Helpers.KnownAssembly.Predicates; -namespace SonarAnalyzer.Helpers +namespace SonarAnalyzer.Helpers; + +public sealed partial class KnownAssembly { - public sealed partial class KnownAssembly - { - private readonly Func, bool> predicate; + private readonly Func, bool> predicate; - public static KnownAssembly XUnit_Assert { get; } = new( - NameIs("xunit.assert"), - NameIs("xunit").And(VersionLowerThen("2.0"))); + public static KnownAssembly XUnit_Assert { get; } = new( + NameIs("xunit.assert"), + NameIs("xunit").And(VersionLowerThen("2.0"))); - internal KnownAssembly(Func predicate, params Func[] or) - : this(predicate is null || or.Any(x => x is null) - ? throw new ArgumentNullException(nameof(predicate), "All predicates must be non-null.") - : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(orPredicate => orPredicate(identitiy)))) - { - } + internal KnownAssembly(Func predicate, params Func[] or) + : this(predicate is null || or.Any(x => x is null) + ? throw new ArgumentNullException(nameof(predicate), "All predicates must be non-null.") + : identities => identities.Any(identitiy => predicate(identitiy) || or.Any(orPredicate => orPredicate(identitiy)))) + { + } - internal KnownAssembly(Func, bool> predicate) => - this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + internal KnownAssembly(Func, bool> predicate) => + this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); - public bool IsReferencedBy(Compilation compilation) => - predicate(compilation.ReferencedAssemblyNames); - } + public bool IsReferencedBy(Compilation compilation) => + predicate(compilation.ReferencedAssemblyNames); } From 98ddc1b0169f17c1c153a8bbee801fad4a8921cb Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:33:25 +0100 Subject: [PATCH 27/31] Extract MockCompilation --- .../Helpers/KnownAssemblyTest.cs | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs index 621efdce555..c1dde5fe92b 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs @@ -62,9 +62,8 @@ public void KnownReference_ThrowsWhenPredicateNull_Params() public void NameIs_Test(string name, bool expected) { var sut = new KnownAssembly(NameIs("Test")); - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity(name); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(identity); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -80,9 +79,7 @@ public void NameIs_Test(string name, bool expected) public void NameContains_Test(string name, bool expected) { var sut = new KnownAssembly(Contains("Test")); - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity(name); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity(name)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -98,9 +95,7 @@ public void NameContains_Test(string name, bool expected) public void NameStartsWith_Test(string name, bool expected) { var sut = new KnownAssembly(StartsWith("Test")); - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity(name); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity(name)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -116,9 +111,7 @@ public void NameStartsWith_Test(string name, bool expected) public void NameEndsWith_Test(string name, bool expected) { var sut = new KnownAssembly(EndsWith("Test")); - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity(name); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity(name)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -131,9 +124,7 @@ public void NameEndsWith_Test(string name, bool expected) [DataRow("3.1.0.0", true)] public void Version_GreaterOrEqual_2_0(string version, bool expected) { - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity("assemblyName", new Version(version)); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity("assemblyName", new Version(version))); var sut = new KnownAssembly(VersionGreaterOrEqual(new Version(2, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(VersionGreaterOrEqual("2.0")); @@ -149,9 +140,7 @@ public void Version_GreaterOrEqual_2_0(string version, bool expected) [DataRow("3.1.0.0", false)] public void Version_LowerThen_2_0(string version, bool expected) { - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity("assemblyName", new Version(version)); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity("assemblyName", new Version(version))); var sut = new KnownAssembly(VersionLowerThen(new Version(2, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(VersionLowerThen("2.0")); @@ -171,9 +160,7 @@ public void Version_LowerThen_2_0(string version, bool expected) [DataRow("10.0.0.0", false)] public void Version_Between_2_0_and_3_5(string version, bool expected) { - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity("assemblyName", new Version(version)); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity("assemblyName", new Version(version))); var sut = new KnownAssembly(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(VersionBetween("2.0.0.0", "3.5.0.0")); @@ -190,7 +177,6 @@ public void Version_Between_2_0_and_3_5(string version, bool expected) [DataRow("AA-68-91-16-d3-a4-ae-33", false)] public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expected) { - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); var identity = new AssemblyIdentity("assemblyName", publicKeyOrToken: ImmutableArray.Create( 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x81, 0xb4, 0x34, 0x5a, 0x02, 0x2c, 0xc0, 0xf4, 0xb4, 0x2b, 0xdc, 0x79, 0x5a, 0x5a, 0x7a, 0x16, 0x23, 0xc1, @@ -199,7 +185,7 @@ public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expect 0xb4, 0x4f, 0x17, 0xa6, 0x37, 0x14, 0xac, 0xed, 0x0b, 0x29, 0xa5, 0x9c, 0xd1, 0xce, 0x58, 0xd8, 0xe1, 0x0c, 0xcd, 0xb6, 0x01, 0x2c, 0x70, 0x98, 0xc3, 0x98, 0x71, 0x04, 0x3b, 0x72, 0x41, 0xac, 0x4a, 0xb9, 0xf6, 0xb3, 0x4f, 0x18, 0x3d, 0xb7, 0x16, 0x08, 0x2c, 0xd5, 0x7c, 0x1f, 0xf6, 0x48, 0x13, 0x5b, 0xec, 0xe2, 0x56, 0x35, 0x7b, 0xa7, 0x35, 0xe6, 0x7d, 0xc6), hasPublicKey: true); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(identity); var sut = new KnownAssembly(PublicKeyTokenIs(publicKeyToken)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(OptionalPublicKeyTokenIs(publicKeyToken)); @@ -209,9 +195,7 @@ public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expect [TestMethod] public void PublicKeyTokenIs_FailsWhenKeyIsMissing() { - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity("assemblyName", hasPublicKey: false)); var sut = new KnownAssembly(PublicKeyTokenIs("c5b62af9de6d7244")); sut.IsReferencedBy(compilation.Object).Should().BeFalse(); } @@ -219,9 +203,7 @@ public void PublicKeyTokenIs_FailsWhenKeyIsMissing() [TestMethod] public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() { - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity("assemblyName", hasPublicKey: false); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + var compilation = MockCompilation(new AssemblyIdentity("assemblyName", hasPublicKey: false)); var sut = new KnownAssembly(OptionalPublicKeyTokenIs("c5b62af9de6d7244")); sut.IsReferencedBy(compilation.Object).Should().BeTrue(); } @@ -236,10 +218,8 @@ public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() [DataRow("Test", "10.0.0.0", false)] public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string name, string version, bool expected) { + var compilation = MockCompilation(new AssemblyIdentity(name, new Version(version))); var sut = new KnownAssembly(StartsWith("Test").And(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0)))); - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity(name, new Version(version)); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -254,10 +234,8 @@ public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string [DataRow("SomethingStart", false)] public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expected) { + var compilation = MockCompilation(new AssemblyIdentity(name)); var sut = new KnownAssembly(StartsWith("Start"), EndsWith("End")); - var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); - var identity = new AssemblyIdentity(name); - compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -292,4 +270,11 @@ public void XUnitAssert_NoReference() .GetCompilation(); compilation.References(XUnit_Assert).Should().BeFalse(); } + + private static Mock MockCompilation(AssemblyIdentity identity) + { + var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); + compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); + return compilation; + } } From 0c21bd562bb28f84c647c8ad2ca77e2ae2ef23b0 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:36:08 +0100 Subject: [PATCH 28/31] Simplify project setup for tests --- .../Helpers/KnownAssemblyTest.cs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs index c1dde5fe92b..d5274f45d2e 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs @@ -242,32 +242,21 @@ public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expect [TestMethod] public void XUnitAssert_2_4() { - var compilation = SolutionBuilder.Create() - .AddProject(AnalyzerLanguage.CSharp) - .AddReferences(NuGetMetadataReference.XunitFramework("2.4.2")) - .AddSnippet("// Empty file") - .GetCompilation(); + var compilation = TestHelper.CompileCS("// Empty file", NuGetMetadataReference.XunitFramework("2.4.2").ToArray()).Model.Compilation; compilation.References(XUnit_Assert).Should().BeTrue(); } [TestMethod] public void XUnitAssert_1_9() { - var compilation = SolutionBuilder.Create() - .AddProject(AnalyzerLanguage.CSharp) - .AddReferences(NuGetMetadataReference.XunitFrameworkV1) - .AddSnippet("// Empty file") - .GetCompilation(); + var compilation = TestHelper.CompileCS("// Empty file", NuGetMetadataReference.XunitFrameworkV1.ToArray()).Model.Compilation; compilation.References(XUnit_Assert).Should().BeTrue(); } [TestMethod] public void XUnitAssert_NoReference() { - var compilation = SolutionBuilder.Create() - .AddProject(AnalyzerLanguage.CSharp) - .AddSnippet("// Empty file") - .GetCompilation(); + var compilation = TestHelper.CompileCS("// Empty file").Model.Compilation; compilation.References(XUnit_Assert).Should().BeFalse(); } From 54ed0dd05e083b89191f351f5fbd863714b5decc Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 12:39:21 +0100 Subject: [PATCH 29/31] Rename method --- .../Helpers/KnownAssemblyTest.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs index d5274f45d2e..eb940bac190 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs @@ -63,7 +63,7 @@ public void NameIs_Test(string name, bool expected) { var sut = new KnownAssembly(NameIs("Test")); var identity = new AssemblyIdentity(name); - var compilation = MockCompilation(identity); + var compilation = CompilationWithReferenceTo(identity); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -79,7 +79,7 @@ public void NameIs_Test(string name, bool expected) public void NameContains_Test(string name, bool expected) { var sut = new KnownAssembly(Contains("Test")); - var compilation = MockCompilation(new AssemblyIdentity(name)); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -95,7 +95,7 @@ public void NameContains_Test(string name, bool expected) public void NameStartsWith_Test(string name, bool expected) { var sut = new KnownAssembly(StartsWith("Test")); - var compilation = MockCompilation(new AssemblyIdentity(name)); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -111,7 +111,7 @@ public void NameStartsWith_Test(string name, bool expected) public void NameEndsWith_Test(string name, bool expected) { var sut = new KnownAssembly(EndsWith("Test")); - var compilation = MockCompilation(new AssemblyIdentity(name)); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -124,7 +124,7 @@ public void NameEndsWith_Test(string name, bool expected) [DataRow("3.1.0.0", true)] public void Version_GreaterOrEqual_2_0(string version, bool expected) { - var compilation = MockCompilation(new AssemblyIdentity("assemblyName", new Version(version))); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity("assemblyName", new Version(version))); var sut = new KnownAssembly(VersionGreaterOrEqual(new Version(2, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(VersionGreaterOrEqual("2.0")); @@ -140,7 +140,7 @@ public void Version_GreaterOrEqual_2_0(string version, bool expected) [DataRow("3.1.0.0", false)] public void Version_LowerThen_2_0(string version, bool expected) { - var compilation = MockCompilation(new AssemblyIdentity("assemblyName", new Version(version))); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity("assemblyName", new Version(version))); var sut = new KnownAssembly(VersionLowerThen(new Version(2, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(VersionLowerThen("2.0")); @@ -160,7 +160,7 @@ public void Version_LowerThen_2_0(string version, bool expected) [DataRow("10.0.0.0", false)] public void Version_Between_2_0_and_3_5(string version, bool expected) { - var compilation = MockCompilation(new AssemblyIdentity("assemblyName", new Version(version))); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity("assemblyName", new Version(version))); var sut = new KnownAssembly(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(VersionBetween("2.0.0.0", "3.5.0.0")); @@ -185,7 +185,7 @@ public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expect 0xb4, 0x4f, 0x17, 0xa6, 0x37, 0x14, 0xac, 0xed, 0x0b, 0x29, 0xa5, 0x9c, 0xd1, 0xce, 0x58, 0xd8, 0xe1, 0x0c, 0xcd, 0xb6, 0x01, 0x2c, 0x70, 0x98, 0xc3, 0x98, 0x71, 0x04, 0x3b, 0x72, 0x41, 0xac, 0x4a, 0xb9, 0xf6, 0xb3, 0x4f, 0x18, 0x3d, 0xb7, 0x16, 0x08, 0x2c, 0xd5, 0x7c, 0x1f, 0xf6, 0x48, 0x13, 0x5b, 0xec, 0xe2, 0x56, 0x35, 0x7b, 0xa7, 0x35, 0xe6, 0x7d, 0xc6), hasPublicKey: true); - var compilation = MockCompilation(identity); + var compilation = CompilationWithReferenceTo(identity); var sut = new KnownAssembly(PublicKeyTokenIs(publicKeyToken)); sut.IsReferencedBy(compilation.Object).Should().Be(expected); sut = new KnownAssembly(OptionalPublicKeyTokenIs(publicKeyToken)); @@ -195,7 +195,7 @@ public void PublicKeyTokenIs_c5b62af9de6d7244(string publicKeyToken, bool expect [TestMethod] public void PublicKeyTokenIs_FailsWhenKeyIsMissing() { - var compilation = MockCompilation(new AssemblyIdentity("assemblyName", hasPublicKey: false)); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity("assemblyName", hasPublicKey: false)); var sut = new KnownAssembly(PublicKeyTokenIs("c5b62af9de6d7244")); sut.IsReferencedBy(compilation.Object).Should().BeFalse(); } @@ -203,7 +203,7 @@ public void PublicKeyTokenIs_FailsWhenKeyIsMissing() [TestMethod] public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() { - var compilation = MockCompilation(new AssemblyIdentity("assemblyName", hasPublicKey: false)); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity("assemblyName", hasPublicKey: false)); var sut = new KnownAssembly(OptionalPublicKeyTokenIs("c5b62af9de6d7244")); sut.IsReferencedBy(compilation.Object).Should().BeTrue(); } @@ -218,7 +218,7 @@ public void OptionalPublicKeyTokenIs_SucceedsWhenKeyIsMissing() [DataRow("Test", "10.0.0.0", false)] public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string name, string version, bool expected) { - var compilation = MockCompilation(new AssemblyIdentity(name, new Version(version))); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name, new Version(version))); var sut = new KnownAssembly(StartsWith("Test").And(VersionBetween(new Version(2, 0, 0, 0), new Version(3, 5, 0, 0)))); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -234,7 +234,7 @@ public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string [DataRow("SomethingStart", false)] public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expected) { - var compilation = MockCompilation(new AssemblyIdentity(name)); + var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name)); var sut = new KnownAssembly(StartsWith("Start"), EndsWith("End")); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } @@ -260,7 +260,7 @@ public void XUnitAssert_NoReference() compilation.References(XUnit_Assert).Should().BeFalse(); } - private static Mock MockCompilation(AssemblyIdentity identity) + private static Mock CompilationWithReferenceTo(AssemblyIdentity identity) { var compilation = new Mock("compilationName", ImmutableArray.Empty, new Dictionary(), false, null, null); compilation.SetupGet(x => x.ReferencedAssemblyNames).Returns(new[] { identity }); From 5117f767d2dfe02daa60b097c060096b47bc4357 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 13 Feb 2023 14:39:51 +0100 Subject: [PATCH 30/31] Remove EditorBrowsable --- .../SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs index dac0fad717b..5aaa53dda82 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.Predicates.cs @@ -18,15 +18,12 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.ComponentModel; - namespace SonarAnalyzer.Helpers; public sealed partial class KnownAssembly { private const StringComparison AssemblyNameComparission = StringComparison.OrdinalIgnoreCase; - [EditorBrowsable(EditorBrowsableState.Never)] internal static class Predicates { internal static Func NameIs(string name) => From cb7633fd835ec5ee8a9867536e6007816b74d505 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 14 Feb 2023 11:36:51 +0100 Subject: [PATCH 31/31] Add public key token to XUnit --- .../Extensions/KnownAssemblyExtensions.cs | 3 +++ .../Helpers/KnownAssembly.cs | 7 +++++-- .../Helpers/KnownAssemblyTest.cs | 19 +++++++++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs b/analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs index 97050f9db03..3051c8a9ae6 100644 --- a/analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/Extensions/KnownAssemblyExtensions.cs @@ -24,4 +24,7 @@ internal static class KnownAssemblyExtensions { internal static Func And(this Func @this, Func predicate) => identity => @this(identity) && predicate(identity); + + internal static Func Or(this Func @this, Func predicate) + => identity => @this(identity) || predicate(identity); } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs index 311017f63df..d7f085bcefd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownAssembly.cs @@ -27,8 +27,8 @@ public sealed partial class KnownAssembly private readonly Func, bool> predicate; public static KnownAssembly XUnit_Assert { get; } = new( - NameIs("xunit.assert"), - NameIs("xunit").And(VersionLowerThen("2.0"))); + And(NameIs("xunit.assert").Or(NameIs("xunit").And(VersionLowerThen("2.0"))), + PublicKeyTokenIs("8d05b1bb7a6fdb6c"))); internal KnownAssembly(Func predicate, params Func[] or) : this(predicate is null || or.Any(x => x is null) @@ -42,4 +42,7 @@ internal KnownAssembly(Func predicate, params Func predicate(compilation.ReferencedAssemblyNames); + + internal static Func And(Func left, Func right) => + KnownAssemblyExtensions.And(left, right); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs index eb940bac190..9170933ef03 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/KnownAssemblyTest.cs @@ -19,7 +19,6 @@ */ using Moq; -using SonarAnalyzer.Common; using SonarAnalyzer.Extensions; using static SonarAnalyzer.Helpers.KnownAssembly; using static SonarAnalyzer.Helpers.KnownAssembly.Predicates; @@ -232,13 +231,29 @@ public void Combinator_NameStartWith_Test_And_Version_Between_2_0_And_3_5(string [DataRow("EndStart", false)] [DataRow("EndSomething", false)] [DataRow("SomethingStart", false)] - public void Combinator_StartsWith_Start_Or_EndsWith_End(string name, bool expected) + public void Combinator_StartsWith_Start_Or_EndsWith_End_1(string name, bool expected) { var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name)); var sut = new KnownAssembly(StartsWith("Start"), EndsWith("End")); sut.IsReferencedBy(compilation.Object).Should().Be(expected); } + [DataTestMethod] + [DataRow("Start", true)] + [DataRow("End", true)] + [DataRow("StartOrEnd", true)] + [DataRow("StartTest", true)] + [DataRow("TestEnd", true)] + [DataRow("EndStart", false)] + [DataRow("EndSomething", false)] + [DataRow("SomethingStart", false)] + public void Combinator_StartsWith_Start_Or_EndsWith_End_2(string name, bool expected) + { + var compilation = CompilationWithReferenceTo(new AssemblyIdentity(name)); + var sut = new KnownAssembly(StartsWith("Start").Or(EndsWith("End"))); + sut.IsReferencedBy(compilation.Object).Should().Be(expected); + } + [TestMethod] public void XUnitAssert_2_4() {