From c6ad5e916fcc431346576fc63aca7219ad7df706 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Thu, 12 Dec 2024 11:50:13 +0100 Subject: [PATCH] Cleanup of custom cfamily implementation --- src/CFamily.UnitTests/CFamilySharedTests.cs | 64 -- .../CompilationDatabaseRequestTests.cs | 396 --------- .../RulesConfigProtocolFormatterTests.cs | 141 --- .../Helpers/DummyCFamilyRulesConfig.cs | 86 -- .../Helpers/DummyExeHelper.cs | 165 ---- ...angAnalyzerTestFile_NoIssues_EmptyFile.txt | 1 - ...rTestFile_NoIssues_EmptyFile_response.json | 3 - .../CLangAnalyzerTestFile_OneIssue.txt | 1 - ...estFile_OneIssue_HasSecondaryLocations.txt | 6 - ...eIssue_HasSecondaryLocations_response.json | 49 - ...angAnalyzerTestFile_OneIssue_response.json | 16 - .../CLangAnalyzerTestFile_TwoIssues.txt | 2 - ...ngAnalyzerTestFile_TwoIssues_response.json | 28 - .../Rules/CFamilyRuleConfigProviderTests.cs | 133 --- ...CFamilySonarWayRulesConfigProviderTests.cs | 107 --- .../Rules/DynamicCFamilyRulesConfigTests.cs | 371 -------- ...ffectiveRulesConfigCalculatorCacheTests.cs | 111 --- .../EffectiveRulesConfigCalculatorTests.cs | 181 ---- .../Rules/RulesConfigFixupTests.cs | 214 ----- .../Rules/RulesLoaderTest.cs | 154 ---- .../RulesLoader/ClassComplexity.json | 22 - .../RulesLoader/ClassComplexity_params.json | 8 - .../RulesLoader/MisraRulesList.json | 4 - .../TestResources/RulesLoader/RulesList.json | 412 --------- .../RulesLoader/Sonar_way_profile.json | 260 ------ .../All_ActiveWithParams_1.json | 23 - .../All_ActiveWithParams_1_params.json | 8 - .../RulesMetadataCache/All_Active_1.json | 25 - .../RulesMetadataCache/All_Inactive_1.json | 25 - .../RulesMetadataCache/C_Active_1.json | 33 - .../RulesMetadataCache/C_Inactive_1.json | 33 - .../RulesMetadataCache/C_Inactive_2.json | 33 - .../RulesMetadataCache/C_Misra_Active_1.json | 33 - .../C_Misra_Inactive_1.json | 33 - .../RulesMetadataCache/Cpp_Active_1.json | 33 - .../RulesMetadataCache/Cpp_Active_2.json | 33 - .../RulesMetadataCache/Cpp_Inactive_1.json | 33 - .../Cpp_Misra_Active_1.json | 33 - .../Cpp_Misra_Inactive_1.json | 33 - .../RulesMetadataCache/MisraRulesList.json | 6 - .../RulesMetadataCache/RulesList.json | 11 - .../RulesMetadataCache/Sonar_way_profile.json | 12 - .../Subprocess/MessageHandlerTests.cs | 264 ------ .../Subprocess/NoOpMessageHandlerTests.cs | 60 -- .../Subprocess/ProcessRunnerTests.cs | 528 ----------- .../Subprocess/ProtocolTest.cs | 569 ------------ .../Analysis/ICFamilyIssueConverterFactory.cs | 36 - src/CFamily/Analysis/IRequest.cs | 49 - src/CFamily/Analysis/IRequestFactory.cs | 41 - src/CFamily/Analysis/RequestContext.cs | 65 -- .../Analysis/RequestFactoryAggregate.cs | 65 -- src/CFamily/CFamily.csproj | 9 - src/CFamily/CFamilyShared.cs | 75 -- .../CompilationDatabaseRequest.cs | 152 ---- .../RulesConfigProtocolFormatter.cs | 85 -- .../Rules/CFamilyRulesConfigProvider.cs | 89 -- .../CFamilySonarWayRulesConfigProvider.cs | 106 --- .../Rules/DynamicCFamilyRulesConfig.cs | 187 ---- .../Rules/EffectiveRulesConfigCalculator.cs | 159 ---- src/CFamily/Rules/ICFamilyRulesConfig.cs | 73 -- .../Rules/ICFamilyRulesConfigProvider.cs | 32 - src/CFamily/Rules/Resources.Designer.cs | 126 --- src/CFamily/Rules/Resources.resx | 145 --- src/CFamily/Rules/RulesConfigFixup.cs | 262 ------ src/CFamily/Rules/RulesLoader.cs | 161 ---- .../Subprocess/CFamilyStrings.Designer.cs | 256 ------ src/CFamily/Subprocess/CFamilyStrings.resx | 189 ---- src/CFamily/Subprocess/IProcessRunner.cs | 27 - src/CFamily/Subprocess/MessageHandler.cs | 159 ---- .../Subprocess/PortedFromJava/Analyzer.cs | 117 --- .../Subprocess/PortedFromJava/Protocol.cs | 238 ----- .../Subprocess/PortedFromJava/ReadMe.txt | 7 - src/CFamily/Subprocess/ProcessRunner.cs | 233 ----- .../Subprocess/ProcessRunnerArguments.cs | 244 ----- src/CFamily/Subprocess/SubProcessFilePaths.cs | 59 -- .../RuleSettingsProviderTests.cs | 152 ---- .../UserRuleSettings/IRuleSettingsProvider.cs | 99 --- .../RuleSettingsProviderFactoryTests.cs | 58 -- .../CFamilyEmbeddedSonarWayRulesTests.cs | 110 --- .../CFamilyIssueConverterFactoryTests.cs | 65 -- ...amilyIssueToAnalysisIssueConverterTests.cs | 840 ------------------ .../Analysis/RuleSettingsProviderFactory.cs | 63 -- .../CFamilyIssueToAnalysisIssueConverter.cs | 326 ------- 83 files changed, 9955 deletions(-) delete mode 100644 src/CFamily.UnitTests/CFamilySharedTests.cs delete mode 100644 src/CFamily.UnitTests/CompilationDatabase/CompilationDatabaseRequestTests.cs delete mode 100644 src/CFamily.UnitTests/CompilationDatabase/RulesConfigProtocolFormatterTests.cs delete mode 100644 src/CFamily.UnitTests/Helpers/DummyCFamilyRulesConfig.cs delete mode 100644 src/CFamily.UnitTests/Helpers/DummyExeHelper.cs delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile.txt delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile_response.json delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue.txt delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations.txt delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations_response.json delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_response.json delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues.txt delete mode 100644 src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues_response.json delete mode 100644 src/CFamily.UnitTests/Rules/CFamilyRuleConfigProviderTests.cs delete mode 100644 src/CFamily.UnitTests/Rules/CFamilySonarWayRulesConfigProviderTests.cs delete mode 100644 src/CFamily.UnitTests/Rules/DynamicCFamilyRulesConfigTests.cs delete mode 100644 src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorCacheTests.cs delete mode 100644 src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorTests.cs delete mode 100644 src/CFamily.UnitTests/Rules/RulesConfigFixupTests.cs delete mode 100644 src/CFamily.UnitTests/Rules/RulesLoaderTest.cs delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity_params.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesLoader/MisraRulesList.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesLoader/RulesList.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesLoader/Sonar_way_profile.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1_params.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Active_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Inactive_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Active_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_2.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Active_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Inactive_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_2.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Inactive_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Active_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Inactive_1.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/MisraRulesList.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/RulesList.json delete mode 100644 src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Sonar_way_profile.json delete mode 100644 src/CFamily.UnitTests/Subprocess/MessageHandlerTests.cs delete mode 100644 src/CFamily.UnitTests/Subprocess/NoOpMessageHandlerTests.cs delete mode 100644 src/CFamily.UnitTests/Subprocess/ProcessRunnerTests.cs delete mode 100644 src/CFamily.UnitTests/Subprocess/ProtocolTest.cs delete mode 100644 src/CFamily/Analysis/ICFamilyIssueConverterFactory.cs delete mode 100644 src/CFamily/Analysis/IRequest.cs delete mode 100644 src/CFamily/Analysis/IRequestFactory.cs delete mode 100644 src/CFamily/Analysis/RequestContext.cs delete mode 100644 src/CFamily/Analysis/RequestFactoryAggregate.cs delete mode 100644 src/CFamily/CFamilyShared.cs delete mode 100644 src/CFamily/CompilationDatabase/CompilationDatabaseRequest.cs delete mode 100644 src/CFamily/CompilationDatabase/RulesConfigProtocolFormatter.cs delete mode 100644 src/CFamily/Rules/CFamilyRulesConfigProvider.cs delete mode 100644 src/CFamily/Rules/CFamilySonarWayRulesConfigProvider.cs delete mode 100644 src/CFamily/Rules/DynamicCFamilyRulesConfig.cs delete mode 100644 src/CFamily/Rules/EffectiveRulesConfigCalculator.cs delete mode 100644 src/CFamily/Rules/ICFamilyRulesConfig.cs delete mode 100644 src/CFamily/Rules/ICFamilyRulesConfigProvider.cs delete mode 100644 src/CFamily/Rules/Resources.Designer.cs delete mode 100644 src/CFamily/Rules/Resources.resx delete mode 100644 src/CFamily/Rules/RulesConfigFixup.cs delete mode 100644 src/CFamily/Rules/RulesLoader.cs delete mode 100644 src/CFamily/Subprocess/CFamilyStrings.Designer.cs delete mode 100644 src/CFamily/Subprocess/CFamilyStrings.resx delete mode 100644 src/CFamily/Subprocess/IProcessRunner.cs delete mode 100644 src/CFamily/Subprocess/MessageHandler.cs delete mode 100644 src/CFamily/Subprocess/PortedFromJava/Analyzer.cs delete mode 100644 src/CFamily/Subprocess/PortedFromJava/Protocol.cs delete mode 100644 src/CFamily/Subprocess/PortedFromJava/ReadMe.txt delete mode 100644 src/CFamily/Subprocess/ProcessRunner.cs delete mode 100644 src/CFamily/Subprocess/ProcessRunnerArguments.cs delete mode 100644 src/CFamily/Subprocess/SubProcessFilePaths.cs delete mode 100644 src/Core.UnitTests/RuleSettingsProviderTests.cs delete mode 100644 src/Core/UserRuleSettings/IRuleSettingsProvider.cs delete mode 100644 src/Integration.Vsix.UnitTests/Analysis/RuleSettingsProviderFactoryTests.cs delete mode 100644 src/Integration.Vsix.UnitTests/CFamily/CFamilyEmbeddedSonarWayRulesTests.cs delete mode 100644 src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueConverterFactoryTests.cs delete mode 100644 src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueToAnalysisIssueConverterTests.cs delete mode 100644 src/Integration.Vsix/Analysis/RuleSettingsProviderFactory.cs delete mode 100644 src/Integration.Vsix/CFamily/CFamilyIssueToAnalysisIssueConverter.cs diff --git a/src/CFamily.UnitTests/CFamilySharedTests.cs b/src/CFamily.UnitTests/CFamilySharedTests.cs deleted file mode 100644 index 4a84ac05a3..0000000000 --- a/src/CFamily.UnitTests/CFamilySharedTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Core; - -namespace SonarLint.VisualStudio.CFamily.UnitTests -{ - [TestClass] - public class CFamilySharedTests - { - [TestMethod] - [DataRow("c:\\aaa.cpp", SonarLanguageKeys.CPlusPlus)] - [DataRow("c:\\AAA.CPP", SonarLanguageKeys.CPlusPlus)] - [DataRow("c:\\aaa.cxx", SonarLanguageKeys.CPlusPlus)] - [DataRow("d:\\xxx.cc", SonarLanguageKeys.CPlusPlus)] - [DataRow("c:\\aaa\\bbb.c", SonarLanguageKeys.C)] - [DataRow("c:\\aaa.cpp.x", null)] - [DataRow("c:\\aaa.cs", null)] - [DataRow("c:\\aaa.js", null)] - [DataRow("c:\\aaa.x", null)] - public void FindLanguage_ReturnsExpectedValue(string filePath, string expected) - { - CFamilyShared.FindLanguageFromExtension(filePath) - .Should().Be(expected); - } - - [TestMethod] - [DataRow("c:\\test.h", true)] - [DataRow("c:\\test.hpp", true)] - [DataRow("c:\\test.hh", true)] - [DataRow("c:\\test.hxx", true)] - [DataRow("c:\\test.H", true)] - [DataRow("c:\\test.HPP", true)] - [DataRow("c:\\test.HH", true)] - [DataRow("c:\\test.HXX", true)] - [DataRow("c:\\test.Hxx", true)] - [DataRow("c:\\test.h.ha", false)] - [DataRow("c:\\test.cpp", false)] - [DataRow("c:\\test.cpp.h", true)] - public void IsHeaderFileExtension_ReturnsExpectedValue(string filePath, bool expected) - { - CFamilyShared.IsHeaderFileExtension(filePath) - .Should().Be(expected); - } - } -} diff --git a/src/CFamily.UnitTests/CompilationDatabase/CompilationDatabaseRequestTests.cs b/src/CFamily.UnitTests/CompilationDatabase/CompilationDatabaseRequestTests.cs deleted file mode 100644 index 72b0599776..0000000000 --- a/src/CFamily.UnitTests/CompilationDatabase/CompilationDatabaseRequestTests.cs +++ /dev/null @@ -1,396 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.CFamily.Analysis; -using SonarLint.VisualStudio.CFamily.CMake; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.CFamily.SubProcess; - -namespace SonarLint.VisualStudio.CFamily.CompilationDatabase.UnitTests -{ - [TestClass] - public class CompilationDatabaseRequestTests - { - private readonly RequestContext ValidContext = new RequestContext("cpp", Mock.Of(), "file.txt", "pchFile.txt", null, false); - private readonly CompilationDatabaseEntry ValidDbEntry = new CompilationDatabaseEntry { File = "file.txt", Directory = "c:\\", Command = "a command" }; - private readonly IReadOnlyDictionary ValidEnvVars = new Dictionary { { "key1", "value1" } }; - - [TestMethod] - public void Ctor_InvalidArguments_Throws() - { - Action act = () => new CompilationDatabaseRequest(null, ValidContext, ValidEnvVars); - act.Should().ThrowExactly().And.ParamName.Should().Be("databaseEntry"); - - act = () => new CompilationDatabaseRequest(ValidDbEntry, null, ValidEnvVars); - act.Should().ThrowExactly().And.ParamName.Should().Be("context"); - } - - [TestMethod] - public void Ctor_NullEnvVars_DoesNotThrow() - { - var testSubject = new CompilationDatabaseRequest(ValidDbEntry, ValidContext, null); - testSubject.EnvironmentVariables.Should().BeNull(); - } - - [TestMethod] - [DataRow(null, "args")] - [DataRow("", "args")] - [DataRow("cmd", null)] - [DataRow("cmd", "")] - public void Ctor_ValidCommandArgsCombination_ShouldNotThrow(string command, string args) - { - var dbEntry = new CompilationDatabaseEntry - { - File = "file", - Directory = "dir", - Command = command, - Arguments = args - }; - - Action act = () => new CompilationDatabaseRequest(dbEntry, ValidContext, ValidEnvVars); - act.Should().NotThrow(); - } - - [TestMethod] - [DataRow(null, null)] - [DataRow("", "")] - [DataRow(null, "")] - [DataRow("", null)] - [DataRow("command", "args")] // can't have both - public void Ctor_InvalidCommandArgsCombination_ShouldThrow(string command, string args) - { - var dbEntry = new CompilationDatabaseEntry - { - File = "file", - Directory = "dir", - Command = command, - Arguments = args - }; - - Action act = () => new CompilationDatabaseRequest(dbEntry, ValidContext, ValidEnvVars); - act.Should().ThrowExactly(); - } - - [TestMethod] - public void WriteRequest_HeaderFile_WritesTheFileFromContext() - { - var dbEntry = new CompilationDatabaseEntry { File = "file.cpp", Directory = "c:\\aaa", Command = "any" }; - var context = new RequestContext("any", Mock.Of(), "file.h", "d:\\preamble.txt", null, true); - - var tokens = WriteRequest(dbEntry, context); - - // File name should be taken from context, to support header files - CheckExpectedSetting(tokens, "File", "file.h"); - } - - [TestMethod] - public void WriteRequest_HeaderFile_WritesHeaderFileLanguage() - { - var dbEntry = new CompilationDatabaseEntry { File = "file.cpp", Directory = "c:\\aaa", Command = "any" }; - var context = new RequestContext("any.language", Mock.Of(), "file.h", "d:\\preamble.txt", null, true); - - var tokens = WriteRequest(dbEntry, context); - - CheckExpectedSetting(tokens, "HeaderFileLanguage", "any.language"); - } - - [TestMethod] - public void WriteRequest_NotHeaderFile_HeaderFileLanguageIsNotWritten() - { - var dbEntry = new CompilationDatabaseEntry { File = "file.cpp", Directory = "c:\\aaa", Command = "any" }; - var context = new RequestContext("any.language", Mock.Of(), "file.cpp", "d:\\preamble.txt", null, false); - - var tokens = WriteRequest(dbEntry, context); - - CheckSettingDoesNotExist(tokens, "HeaderFileLanguage"); - } - - [TestMethod] - public void WriteRequest_ValidRequest_ExpectedHeaderFooterAndSimpleProperties() - { - var dbEntry = new CompilationDatabaseEntry { File = "file.txt", Directory = "c:\\aaa", Command = "any" }; - var context = new RequestContext("any", Mock.Of(), "file.h", "d:\\preamble.txt", null, true); - - var tokens = WriteRequest(dbEntry, context); - - // Header and footer - tokens.First().Should().Be("SL-IN"); - tokens.Last().Should().Be("SL-END"); - - // Simple properties i.e. ones that are just written as-is - CheckExpectedSetting(tokens, "File", "file.h"); - CheckExpectedSetting(tokens, "Directory", "c:\\aaa"); - CheckExpectedSetting(tokens, "PreambleFile", "d:\\preamble.txt"); - } - - [TestMethod] - public void WriteRequest_WithCommand_ExpectedSettingWritten() - { - var dbEntry = new CompilationDatabaseEntry { File = "file.txt", Directory = "c:\\aaa", Command = "cmd1 cmd2" }; - - var tokens = WriteRequest(dbEntry, ValidContext); - - CheckExpectedSetting(tokens, "Command", "cmd1 cmd2"); - CheckSettingDoesNotExist(tokens, "Arguments"); - } - - [TestMethod] - public void WriteRequest_WithArguments_ExpectedSettingWritten() - { - var dbEntry = new CompilationDatabaseEntry { File = "file.txt", Directory = "c:\\aaa", Arguments = "arg1\narg2" }; - - var tokens = WriteRequest(dbEntry, ValidContext); - - CheckExpectedSetting(tokens, "Arguments", "arg1\narg2"); - CheckSettingDoesNotExist(tokens, "Commands"); - } - - [TestMethod] - [DataRow(true, "true")] - [DataRow(false, "false")] - public void WriteRequest_CreateReproducer_ExpectedSettingWritten(bool createReproducer, string expectedValue) - { - var analyzerOptions = new CFamilyAnalyzerOptions { CreateReproducer = createReproducer }; - var context = CreateContext(analyzerOptions: analyzerOptions); - - var tokens = WriteRequest(ValidDbEntry, context); - - CheckExpectedSetting(tokens, "CreateReproducer", expectedValue); - } - - [TestMethod] - [DataRow(true, "true")] - [DataRow(false, "false")] - public void WriteRequest_CreatePreamble_ExpectedSettingWritten(bool createPch, string expectedValue) - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp") - .AddRule("active1", isActive: true); - - var analyzerOptions = new CFamilyAnalyzerOptions { CreatePreCompiledHeaders = createPch }; - var context = CreateContext(rulesConfig: rulesConfig, analyzerOptions: analyzerOptions); - - var tokens = WriteRequest(ValidDbEntry, context); - - CheckExpectedSetting(tokens, "BuildPreamble", expectedValue); - - if (createPch) - { - CheckExpectedSetting(tokens, "QualityProfile", string.Empty); - } - else - { - CheckExpectedSetting(tokens, "QualityProfile", "active1"); - } - } - - [TestMethod] - public void WriteRequest_QualityProfile_ExpectedSettingWritten() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp") - .AddRule("inactive1", isActive: false) - .AddRule("active1", isActive: true) - .AddRule("active2", isActive: true) - .AddRule("inactive2", isActive: false); - - var context = CreateContext(rulesConfig: rulesConfig); - - var tokens = WriteRequest(ValidDbEntry, context); - - CheckExpectedSetting(tokens, "QualityProfile", "active1,active2"); - } - - [TestMethod] - public void WriteRequest_WithRuleParameters_ExpectedSettingWritten() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp") - .AddRule("inactive1", isActive: false, - parameters: new Dictionary - { - { "XXX1", "VVV1"} - }) - - // Active rule with no parameters - .AddRule("active1", isActive: true) - - // Active rule, one parameter - .AddRule("active2", isActive: true, - parameters: new Dictionary - { - { "key2_1", "value2_1" } - }) - - .AddRule("inactive2", isActive: false, - parameters: new Dictionary - { - { "XXX2", "VVV2"}, - { "XXX3", "VVV3"} - }) - - // Active rule, multiple parameters - .AddRule("active3", isActive: true, - parameters: new Dictionary - { - { "key3_1", "value3_1" }, - { "key3_2", "value3_2" } - }); - - var context = CreateContext(rulesConfig: rulesConfig); - - var tokens = WriteRequest(ValidDbEntry, context); - - CheckExpectedSetting(tokens, "active2.key2_1", "value2_1"); - CheckExpectedSetting(tokens, "active3.key3_1", "value3_1"); - CheckExpectedSetting(tokens, "active3.key3_2", "value3_2"); - - CheckSettingDoesNotExist(tokens, "inactive1"); - CheckSettingDoesNotExist(tokens, "inactive2"); - CheckSettingDoesNotExist(tokens, "active1"); - CheckSettingDoesNotExist(tokens, "active4"); - - } - - [TestMethod] - public void WriteRequest_NoRuleParameters_NoErrors() - { - // Active rules with no parameters - var rulesConfig = new DummyCFamilyRulesConfig("cpp") - .AddRule("active1", isActive: true) - .AddRule("active2", isActive: true); - - var context = CreateContext(rulesConfig: rulesConfig); - - var tokens = WriteRequest(ValidDbEntry, context); - - tokens.Where(x => x.StartsWith("active1.")).Should().BeEmpty(); - tokens.Where(x => x.StartsWith("active2.")).Should().BeEmpty(); - } - - [TestMethod] - public void WriteDiagnostics_ExpectedDataWritten() - { - // Expecting the context to be ignored - var context = CreateContext("foo", Mock.Of(), "some file", "some pch file", - new CFamilyAnalyzerOptions { CreatePreCompiledHeaders = true }); - - var dbEntry = new CompilationDatabaseEntry - { - File = "c:\\file.txt", Directory = "d:\\", Command = "1\n2\n" - }; - var expected = @"{ - ""directory"": ""d:\\"", - ""command"": ""1\n2\n"", - ""file"": ""c:\\file.txt"", - ""arguments"": null -}"; - - var testSubject = new CompilationDatabaseRequest(dbEntry, context, ValidEnvVars); - - var sb = new StringBuilder(); - using (var writer = new StringWriter(sb)) - { - testSubject.WriteRequestDiagnostics(writer); - }; - - var actual = sb.ToString(); - actual.Should().Be(expected); - } - - [TestMethod] - public void EnvironmentVariables_ReturnsExpectedValues() - { - var envVars = new Dictionary { { "INCLUDE", "" }, { "PATH", "any"} }; - var testSubject = new CompilationDatabaseRequest(ValidDbEntry, ValidContext, envVars); - - var actual = testSubject.EnvironmentVariables; - - actual.Count.Should().Be(2); - actual.Keys.Should().BeEquivalentTo("INCLUDE", "PATH"); - actual["INCLUDE"].Should().BeEmpty(); - actual["PATH"].Should().Be("any"); - } - - private static RequestContext CreateContext( - string language = "c", - ICFamilyRulesConfig rulesConfig = null, - string file = "file.txt", - string pchFile = "pch.txt", - CFamilyAnalyzerOptions analyzerOptions = null) - { - return new RequestContext( - language: language, - rulesConfig: rulesConfig ?? Mock.Of(), - file: file, - pchFile: pchFile, - analyzerOptions: analyzerOptions, false); - } - - /// - /// Executes the request, and returns the ordered list of strings that were - /// written to the binary stream - /// - private IList WriteRequest(CompilationDatabaseEntry dbEntry, RequestContext context) - { - var tokens = new List(); - - var testSubject = new CompilationDatabaseRequest(dbEntry, context, ValidEnvVars); - - using (var stream = new MemoryStream()) - { - using (var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true)) - { - testSubject.WriteRequest(writer); - } - stream.Flush(); - var endOfStreamPosition = stream.Position; - stream.Position = 0; - - using (var reader = new BinaryReader(stream)) - { - while(stream.Position != endOfStreamPosition) - { - tokens.Add(Protocol.ReadUTF(reader)); - } - } - } - - return tokens; - } - - private void CheckExpectedSetting(IList tokens, string key, string value) - { - var keyIndex = tokens.IndexOf(key); - keyIndex.Should().NotBe(-1); - keyIndex.Should().NotBe(tokens.Count - 2); - tokens[keyIndex + 1].Should().Be(value); - } - - private void CheckSettingDoesNotExist(IList tokens, string key) => - tokens.IndexOf(key).Should().Be(-1); - } -} diff --git a/src/CFamily.UnitTests/CompilationDatabase/RulesConfigProtocolFormatterTests.cs b/src/CFamily.UnitTests/CompilationDatabase/RulesConfigProtocolFormatterTests.cs deleted file mode 100644 index 8d237329fd..0000000000 --- a/src/CFamily.UnitTests/CompilationDatabase/RulesConfigProtocolFormatterTests.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; - -namespace SonarLint.VisualStudio.CFamily.CompilationDatabase.UnitTests -{ - [TestClass] - public class RulesConfigProtocolFormatterTests - { - [TestMethod] - public void Format_NullRulesConfig_ArgumentNullException() - { - var testSubject = CreateTestSubject(); - - Action act = () => testSubject.Format(null); - - act.Should().Throw().And.ParamName.Should().Be("rulesConfig"); - } - - [TestMethod] - public void Format_NoRules_EmptyQualityProfile() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp"); - - var testSubject = CreateTestSubject(); - var result = testSubject.Format(rulesConfig); - - result.QualityProfile.Should().BeEmpty(); - } - - [TestMethod] - public void Format_NoActiveRules_EmptyQualityProfile() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp"); - rulesConfig.AddRule("123", false); - - var testSubject = CreateTestSubject(); - var result = testSubject.Format(rulesConfig); - - result.QualityProfile.Should().BeEmpty(); - } - - [TestMethod] - public void Format_OneActiveRule_OneRuleInQualityProfile() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp"); - rulesConfig.AddRule("123", true); - - var testSubject = CreateTestSubject(); - var result = testSubject.Format(rulesConfig); - - result.QualityProfile.Should().Be("123"); - } - - [TestMethod] - public void Format_MultipleActiveRules_CommaSeparatedQualityProfile() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp"); - rulesConfig.AddRule("12", true); - rulesConfig.AddRule("34", false); - rulesConfig.AddRule("56", true); - rulesConfig.AddRule("78", false); - - var testSubject = CreateTestSubject(); - var result = testSubject.Format(rulesConfig); - - result.QualityProfile.Should().Be("12,56"); - } - - [TestMethod] - public void Format_NoRules_EmptyRuleParameters() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp"); - - var testSubject = CreateTestSubject(); - var result = testSubject.Format(rulesConfig); - - result.RuleParameters.Should().BeEmpty(); - } - - [TestMethod] - public void Format_MultipleRules_DotSeparatedParametersForActiveRules() - { - var rulesConfig = new DummyCFamilyRulesConfig("cpp"); - - rulesConfig.AddRule("rule1", true, new Dictionary - { - {"param1", "some value"}, - {"param2", "some other value"} - }); - - // inactive rules should be ignored - rulesConfig.AddRule("inactive", false, new Dictionary - { - {"param3", "value3"}, - {"param4", "value4"} - }); - - rulesConfig.AddRule("rule2", true, new Dictionary - { - {"some param", "value1"}, - {"some other param", "value2"} - }); - - var testSubject = CreateTestSubject(); - var result = testSubject.Format(rulesConfig); - - result.RuleParameters.Should().BeEquivalentTo(new Dictionary - { - {"rule1.param1", "some value"}, - {"rule1.param2", "some other value"}, - {"rule2.some param", "value1"}, - {"rule2.some other param", "value2"} - }); - } - - private RulesConfigProtocolFormatter CreateTestSubject() => new RulesConfigProtocolFormatter(); - } -} diff --git a/src/CFamily.UnitTests/Helpers/DummyCFamilyRulesConfig.cs b/src/CFamily.UnitTests/Helpers/DummyCFamilyRulesConfig.cs deleted file mode 100644 index d5d9824f9e..0000000000 --- a/src/CFamily.UnitTests/Helpers/DummyCFamilyRulesConfig.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Collections.Generic; -using System.Linq; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Helpers.UnitTests -{ - public class DummyCFamilyRulesConfig : ICFamilyRulesConfig - { - private readonly IDictionary ruleKeyToActiveMap; - - public DummyCFamilyRulesConfig(string languageKey) - { - LanguageKey = languageKey; - ruleKeyToActiveMap = new Dictionary(); - } - - public DummyCFamilyRulesConfig AddRule(string partialRuleKey, IssueSeverity issueSeverity, bool isActive, Code code) - { - return AddRule(partialRuleKey, issueSeverity, isActive, null, code); - } - - public DummyCFamilyRulesConfig AddRule(string partialRuleKey, bool isActive) - { - ruleKeyToActiveMap[partialRuleKey] = isActive; - RulesMetadata[partialRuleKey] = new RuleMetadata(); - return this; - } - - public DummyCFamilyRulesConfig AddRule(string partialRuleKey, bool isActive, Dictionary parameters) - { - return AddRule(partialRuleKey, (IssueSeverity)0 /* default enum value */, isActive, parameters, new Code()); - } - - public DummyCFamilyRulesConfig AddRule(string partialRuleKey, IssueSeverity issueSeverity, bool isActive, Dictionary parameters, Code code) - { - ruleKeyToActiveMap[partialRuleKey] = isActive; - RulesMetadata[partialRuleKey] = new RuleMetadata { DefaultSeverity = issueSeverity, Code = code }; - - if (parameters != null) - { - RulesParameters[partialRuleKey] = parameters; - } - return this; - } - - #region IRulesConfiguration interface - - public string LanguageKey { get; set; } - - public IEnumerable AllPartialRuleKeys => ruleKeyToActiveMap.Keys; - - public IEnumerable ActivePartialRuleKeys => ruleKeyToActiveMap.Where(kvp => kvp.Value) - .Select(kvp => kvp.Key) - .ToList(); - - public IDictionary> RulesParameters { get; set; } - = new Dictionary>(); - - public IDictionary RulesMetadata { get; set; } - = new Dictionary(); - - #endregion - } -} diff --git a/src/CFamily.UnitTests/Helpers/DummyExeHelper.cs b/src/CFamily.UnitTests/Helpers/DummyExeHelper.cs deleted file mode 100644 index b07c69f864..0000000000 --- a/src/CFamily.UnitTests/Helpers/DummyExeHelper.cs +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.CodeDom.Compiler; -using System.IO; -using FluentAssertions; -using Microsoft.CSharp; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -// Note: copied from the S4MSB -// https://github.com/SonarSource/sonar-scanner-msbuild/blob/5c23a7da9171e90a1970a31507dce3da3e8ee094/Tests/TestUtilities/DummyExeHelper.cs#L34 - -namespace SonarLint.VisualStudio.CFamily.Helpers.UnitTests -{ - /// - /// Creates dummy executables that log the input parameters and return a specified - /// exit code - /// - internal static class DummyExeHelper - { - private const string DummyExeName = "dummy.exe"; - - #region Public methods - - public static string CreateDummyExe(string outputDir, string exeName, int exitCode) - { - return CreateDummyExe(outputDir, exeName, exitCode, null); - } - - public static string CreateDummyExe(string outputDir, string exeName, int exitCode, string additionalCode) - { - var code = GetDummyExeSource(exitCode, additionalCode); - var asmPath = Path.Combine(outputDir, exeName); - CompileAssembly(code, asmPath); - return asmPath; - } - - public static string CreateDummyExe(string outputDir, int exitCode) - { - return CreateDummyExe(outputDir, DummyExeName, exitCode, null); - } - - #endregion Public methods - - #region Checks - - public static string AssertDummyExeLogExists(string dummyBinDir, TestContext testContext) - { - var logFilePath = GetLogFilePath(dummyBinDir, DummyExeName); - return AssertLogFileExists(logFilePath, testContext); - } - - - public static string GetLogFilePath(string dummyBinDir, string exeName) - { - var logFilePath = Path.Combine(dummyBinDir, exeName); - logFilePath = Path.ChangeExtension(logFilePath, ".log"); - return logFilePath; - } - - public static void AssertExpectedLogContents(string logPath, params string[] expected) - { - File.Exists(logPath).Should().BeTrue("Expected log file does not exist: {0}", logPath); - - var actualLines = File.ReadAllLines(logPath); - - (expected ?? new string[] { }).Should().BeEquivalentTo(actualLines, "Log file does not have the expected content"); - } - - public static string AssertLogFileExists(string logFilePath, TestContext testContext) - { - File.Exists(logFilePath).Should().BeTrue("Expecting the dummy exe log to exist. File: {0}", logFilePath); - testContext.AddResultFile(logFilePath); - return logFilePath; - } - - public static string AssertLogFileDoesNotExist(string dummyBinDir, string exeName) - { - var logFilePath = GetLogFilePath(dummyBinDir, exeName); - - File.Exists(logFilePath).Should().BeFalse("Not expecting the dummy exe log to exist. File: {0}", logFilePath); - return logFilePath; - } - - #endregion Checks - - #region Private methods - - private static string GetDummyExeSource(int returnCode, string additionalCode) - { - string code = @" -using System; -using System.IO; - -namespace SonarQube.Bootstrapper.Tests.Dummy -{ - class Program - { - static int Main(string[] args) - { - string logFile = Path.ChangeExtension(Path.Combine(typeof(Program).Assembly.Location), ""log""); - - File.WriteAllLines(logFile, args); - - int exitCode = EXITCODE_PLACEHOLDER; - - ADDITIONALCODE_PLACEHOLDER - - return exitCode; - } - } -}"; - code = code.Replace("EXITCODE_PLACEHOLDER", returnCode.ToString()); - code = code.Replace("ADDITIONALCODE_PLACEHOLDER", additionalCode); - return code; - } - - /// - /// Compiles the supplied code into a new assembly - /// - private static void CompileAssembly(string code, string outputFilePath) - { - var provider = new CSharpCodeProvider(); - - var options = new CompilerParameters - { - OutputAssembly = outputFilePath, - GenerateExecutable = true, - GenerateInMemory = false - }; - - var result = provider.CompileAssemblyFromSource(options, code); - - if (result.Errors.Count > 0) - { - foreach(var item in result.Output) - { - Console.WriteLine(item); - } - - Assert.Fail("Test setup error: failed to create dynamic assembly. See the test output for compiler output"); - } - } - - #endregion Private methods - } -} diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile.txt b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile.txt deleted file mode 100644 index 5f282702bb..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile_response.json b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile_response.json deleted file mode 100644 index 4643e632d3..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_NoIssues_EmptyFile_response.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Messages": [] -} \ No newline at end of file diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue.txt b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue.txt deleted file mode 100644 index 48340c4e67..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue.txt +++ /dev/null @@ -1 +0,0 @@ -// todo: this line should raise an issue diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations.txt b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations.txt deleted file mode 100644 index d34294e6b0..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations.txt +++ /dev/null @@ -1,6 +0,0 @@ -void a(bool b) { - int* f = nullptr; - if (b) { - *f = 2; - } -} \ No newline at end of file diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations_response.json b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations_response.json deleted file mode 100644 index 069067e5e5..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_HasSecondaryLocations_response.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "Messages": [ - { - "Filename": "", - "Fixes": [], - "RuleKey": "S2259", - "PartsMakeFlow": true, - "Parts": [ - { - "Filename": "", - "Line": 4, - "Column": 12, - "EndLine": 4, - "EndColumn": 13, - "Text": "Dereference of null pointer (loaded from variable 'f')" - }, - { - "Filename": "", - "Line": 3, - "Column": 5, - "EndLine": 3, - "EndColumn": 7, - "Text": "Taking true branch" - }, - { - "Filename": "", - "Line": 3, - "Column": 9, - "EndLine": 3, - "EndColumn": 10, - "Text": "Assuming 'b' is true" - }, - { - "Filename": "", - "Line": 2, - "Column": 5, - "EndLine": 2, - "EndColumn": 11, - "Text": "'f' initialized to a null pointer value" - } - ], - "Line": 4, - "Column": 12, - "EndLine": 4, - "EndColumn": 13, - "Text": "Dereference of null pointer (loaded from variable 'f')" - } - ] -} \ No newline at end of file diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_response.json b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_response.json deleted file mode 100644 index 135b1bf28b..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_OneIssue_response.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Messages": [ - { - "Filename": "", - "Fixes": [], - "RuleKey": "S1135", - "PartsMakeFlow": false, - "Parts": [], - "Line": 1, - "Column": 4, - "EndLine": 1, - "EndColumn": 44, - "Text": "Complete the task associated to this \"todo\" comment." - } - ] -} \ No newline at end of file diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues.txt b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues.txt deleted file mode 100644 index 96fce12726..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues.txt +++ /dev/null @@ -1,2 +0,0 @@ -// todo: this line should raise an issue - // todo: this other line should also raise an issue diff --git a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues_response.json b/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues_response.json deleted file mode 100644 index e19277393f..0000000000 --- a/src/CFamily.UnitTests/IntegrationTests/CLangAnalyzerTestFile_TwoIssues_response.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "Messages": [ - { - "Filename": "", - "Fixes": [], - "RuleKey": "S1135", - "PartsMakeFlow": false, - "Parts": [], - "Line": 1, - "Column": 4, - "EndLine": 1, - "EndColumn": 44, - "Text": "Complete the task associated to this \"todo\" comment." - }, - { - "Filename": "", - "Fixes": [], - "RuleKey": "S1135", - "PartsMakeFlow": false, - "Parts": [], - "Line": 2, - "Column": 4, - "EndLine": 2, - "EndColumn": 55, - "Text": "Complete the task associated to this \"todo\" comment." - } - ] -} \ No newline at end of file diff --git a/src/CFamily.UnitTests/Rules/CFamilyRuleConfigProviderTests.cs b/src/CFamily.UnitTests/Rules/CFamilyRuleConfigProviderTests.cs deleted file mode 100644 index a42895955d..0000000000 --- a/src/CFamily.UnitTests/Rules/CFamilyRuleConfigProviderTests.cs +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class CFamilyRuleConfigProviderTests - { - [TestMethod] - public void MefCtor_CheckExports() - { - MefTestHelpers.CheckTypeCanBeImported( - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport()); - } - - [TestMethod] - public void Get_NullLanguage_ArgumentNullException() - { - var testSubject = CreateTestSubject(new RulesSettings(), new DummyCFamilyRulesConfig("cpp")); - - Action act = () => testSubject.GetRulesConfiguration(null); - - act.Should().Throw().And.ParamName.Should().Be("languageKey"); - } - - [TestMethod] - public void Get_UnknownLanguageKey_ArgumentNullException() - { - var testSubject = CreateTestSubject(new RulesSettings(), new DummyCFamilyRulesConfig("cpp")); - - Action act = () => testSubject.GetRulesConfiguration("sfdsfdggretert"); - - act.Should().Throw().And.ParamName.Should().Be("language"); - } - - [DataRow(true)] - [DataRow(false)] - [DataTestMethod] - public void Get_EffectiveRulesAreCalculatedDependingOnHotspotConfiguration(bool hotspotsEnabled) - { - var fullHotspotRuleKey = RulesConfigFixup.HotspotRulesKeys[0]; - var hotspotRuleKey = fullHotspotRuleKey.Split(':')[1]; - var standaloneModeSettings = new RulesSettings - { - Rules = new Dictionary - { - {"cpp:rule1", new RuleConfig {Level = RuleLevel.On}}, - {"cpp:rule2", new RuleConfig {Level = RuleLevel.Off}}, - {"cpp:rule4", new RuleConfig {Level = RuleLevel.On}}, - {"XXX:rule3", new RuleConfig {Level = RuleLevel.On}}, - {fullHotspotRuleKey, new RuleConfig {Level = RuleLevel.On}}, - } - }; - - var sonarWayConfig = new DummyCFamilyRulesConfig("cpp") - .AddRule("rule1", IssueSeverity.Info, isActive: false, code: null) - .AddRule("rule2", IssueSeverity.Major, isActive: false, code: null) - .AddRule("rule3", IssueSeverity.Minor, isActive: true, code: null) - .AddRule("rule4", IssueSeverity.Blocker, isActive: false, code: null) - .AddRule(hotspotRuleKey, IssueSeverity.Blocker, isActive: true, code: null); - - var testSubject = CreateTestSubject(standaloneModeSettings, sonarWayConfig, hotspotsEnabled); - - // Act - var result = testSubject.GetRulesConfiguration("cpp"); - - // Assert - var activeKeys = new List { "rule1", "rule3", "rule4" }; - - if (hotspotsEnabled) - { - activeKeys.Add(hotspotRuleKey); - } - - result.ActivePartialRuleKeys.Should().BeEquivalentTo(activeKeys); - result.AllPartialRuleKeys.Should().BeEquivalentTo("rule1", "rule2", "rule3", "rule4", hotspotRuleKey); - } - - private CFamilyRuleConfigProvider CreateTestSubject(RulesSettings ruleSettings, DummyCFamilyRulesConfig sonarWayConfig, bool enableHotspots = false) - { - var ruleSettingsProvider = new Mock(); - ruleSettingsProvider.Setup(x => x.Get()).Returns(ruleSettings); - - var ruleSettingsProviderFactory = new Mock(); - ruleSettingsProviderFactory.Setup(x => x.Get(Language.Cpp)).Returns(ruleSettingsProvider.Object); - - var sonarWayProviderMock = new Mock(); - - sonarWayProviderMock.Setup(x => x.GetRulesConfiguration(It.IsAny())) - .Returns(sonarWayConfig); - - var hotspotAnalysisConfigurationMock = new Mock(); - hotspotAnalysisConfigurationMock.Setup(x => x.IsHotspotsAnalysisEnabled()).Returns(enableHotspots); - - var testSubject = new CFamilyRuleConfigProvider(ruleSettingsProviderFactory.Object, - sonarWayProviderMock.Object, - hotspotAnalysisConfigurationMock.Object, - Mock.Of()); - - return testSubject; - } - } -} diff --git a/src/CFamily.UnitTests/Rules/CFamilySonarWayRulesConfigProviderTests.cs b/src/CFamily.UnitTests/Rules/CFamilySonarWayRulesConfigProviderTests.cs deleted file mode 100644 index 3aec358a71..0000000000 --- a/src/CFamily.UnitTests/Rules/CFamilySonarWayRulesConfigProviderTests.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.IO; -using FluentAssertions; -using FluentAssertions.Execution; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class CFamilySonarWayRulesConfigProviderTests - { - // Rule data for files in Rules\TestResources\RulesMetadataCache - private const int Active_C_Rules = 3; - private const int Inactive_C_Rules = 3; - private const int Active_Misra_C_Rules = 1; - private const int Inactive_Misra_C_Rules = 1; - - private const int Active_CPP_Rules = 4; - private const int Inactive_CPP_Rules = 2; - private const int Active_Misra_Cpp_Rules = 1; - private const int Inactive_Misra_Cpp_Rules = 1; - - private CFamilySonarWayRulesConfigProvider sonarWayProvider = CreateTestSubject(); - - [TestMethod] - public void Settings_LanguageKey() - { - sonarWayProvider.GetRulesConfiguration("c").LanguageKey.Should().Be("c"); - sonarWayProvider.GetRulesConfiguration("cpp").LanguageKey.Should().Be("cpp"); - - // We don't currently support ObjC rules in VS - sonarWayProvider.GetRulesConfiguration("objc").Should().BeNull(); - } - - [TestMethod] - public void Read_Rules() - { - sonarWayProvider.GetRulesConfiguration("c").AllPartialRuleKeys.Should().HaveCount(Active_C_Rules + Inactive_C_Rules + Active_Misra_C_Rules + Inactive_Misra_C_Rules); - sonarWayProvider.GetRulesConfiguration("cpp").AllPartialRuleKeys.Should().HaveCount(Active_CPP_Rules + Inactive_CPP_Rules + Active_Misra_Cpp_Rules + Inactive_Misra_Cpp_Rules); - - // We don't currently support ObjC rules in VS - sonarWayProvider.GetRulesConfiguration("objc").Should().BeNull(); - } - - [TestMethod] - public void Read_Active_Rules() - { - sonarWayProvider.GetRulesConfiguration("c").ActivePartialRuleKeys.Should().HaveCount(Active_C_Rules + Active_Misra_C_Rules); - sonarWayProvider.GetRulesConfiguration("cpp").ActivePartialRuleKeys.Should().HaveCount(Active_CPP_Rules + Active_Misra_Cpp_Rules); - - // We don't currently support ObjC rules in VS - sonarWayProvider.GetRulesConfiguration("objc").Should().BeNull(); - } - - [TestMethod] - public void Read_Rules_Params() - { - sonarWayProvider.GetRulesConfiguration("cpp").RulesParameters.TryGetValue("All_ActiveWithParams_1", out var parameters); - parameters.Should() - .Contain(new System.Collections.Generic.KeyValuePair("maximumClassComplexityThreshold", "80")); - } - - [TestMethod] - public void Read_Rules_Metadata() - { - sonarWayProvider.GetRulesConfiguration("cpp").RulesMetadata.TryGetValue("All_ActiveWithParams_1", out var metadata); - using (new AssertionScope()) - { - metadata.Type.Should().Be(IssueType.CodeSmell); - metadata.DefaultSeverity.Should().Be(IssueSeverity.Critical); - } - } - - private static CFamilySonarWayRulesConfigProvider CreateTestSubject() - { - var resourcesPath = Path.Combine( - Path.GetDirectoryName(typeof(CFamilySonarWayRulesConfigProvider).Assembly.Location), - "Rules", "TestResources", "RulesMetadataCache"); - Directory.Exists(resourcesPath).Should().BeTrue($"Test setup error: expected test resources directory does not exist: {resourcesPath}"); - - var testSubject = new CFamilySonarWayRulesConfigProvider(resourcesPath); - return testSubject; - } - - } -} diff --git a/src/CFamily.UnitTests/Rules/DynamicCFamilyRulesConfigTests.cs b/src/CFamily.UnitTests/Rules/DynamicCFamilyRulesConfigTests.cs deleted file mode 100644 index 5fa18fc60a..0000000000 --- a/src/CFamily.UnitTests/Rules/DynamicCFamilyRulesConfigTests.cs +++ /dev/null @@ -1,371 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Analysis; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class DynamicCFamilyRulesConfigTests - { - [TestMethod] - public void Ctor_NullArguments_Throws() - { - var settings = new RulesSettings(); - - // 1. Default rules config - Action act = () => new DynamicCFamilyRulesConfig(null, - settings, - Mock.Of(), - new TestLogger()); - act.Should().ThrowExactly().And.ParamName.Should().Be("defaultRulesConfig"); - - // 2. Custom settings - act = () => new DynamicCFamilyRulesConfig(new DummyCFamilyRulesConfig("anyLanguage"), - null, - Mock.Of(), - new TestLogger()); - act.Should().ThrowExactly().And.ParamName.Should().Be("customRulesSettings"); - - // 3. Logger - act = () => new DynamicCFamilyRulesConfig(new DummyCFamilyRulesConfig("anyLanguage"), - settings, - Mock.Of(), - null); - act.Should().ThrowExactly().And.ParamName.Should().Be("logger"); - - // 4. Hotspot config - act = () => new DynamicCFamilyRulesConfig(new DummyCFamilyRulesConfig("anyLanguage"), - settings, - null, - new TestLogger()); - act.Should().ThrowExactly().And.ParamName.Should().Be("connectedModeFeaturesConfiguration"); - } - - [TestMethod] - public void NullOrEmptyRulesSettings_DefaultsUsed() - { - // Arrange - var defaultConfig = new DummyCFamilyRulesConfig("123") - .AddRule("rule1", IssueSeverity.Blocker, isActive: true, - parameters: new Dictionary { { "p1", "v1" } }, - code: new Code { Impacts = new Dictionary { { SoftwareQuality.Maintainability, SoftwareQualitySeverity.High } } }) - .AddRule("rule2", IssueSeverity.Major, isActive: true, - parameters: new Dictionary { { "p2", "v2" } }, - code: new Code { Impacts = new Dictionary { { SoftwareQuality.Maintainability, SoftwareQualitySeverity.Medium } } }) - .AddRule("rule3", IssueSeverity.Minor, isActive: false, - parameters: new Dictionary { { "p3", "v3" } }, - code: new Code { Impacts = new Dictionary { { SoftwareQuality.Maintainability, SoftwareQualitySeverity.Low } } }); - - var settings = new RulesSettings(); - - // Act - using (new AssertIgnoreScope()) - { - var testSubject = CreateTestSubject(defaultConfig, settings); - - // Assert - testSubject.AllPartialRuleKeys.Should().BeEquivalentTo("rule1", "rule2", "rule3"); - testSubject.ActivePartialRuleKeys.Should().BeEquivalentTo("rule1", "rule2"); - - testSubject.LanguageKey.Should().Be("123"); - - // Other properties should be pass-throughs - testSubject.AllPartialRuleKeys.Should().BeEquivalentTo(defaultConfig.AllPartialRuleKeys); - testSubject.RulesParameters.Should().BeEquivalentTo(defaultConfig.RulesParameters); - testSubject.RulesMetadata.Should().BeEquivalentTo(defaultConfig.RulesMetadata); - } - } - - [TestMethod] - public void RuleConfigFixup_IsCalled() - { - // Arrange - var defaultConfig = new DummyCFamilyRulesConfig("123") - .AddRule("rule1", isActive: true, null) - .AddRule("rule2", isActive: true, null); - - var inputSettings = new RulesSettings(); - - var hotspotAnalysisConfigurationMock = Mock.Of(); - - // Fixup that should disable rule1 - var fixedUpSettings = new RulesSettings - { - Rules = { { "123:rule1", new RuleConfig { Level = RuleLevel.Off } } } - }; - var fixup = new Mock(); - fixup - .Setup(x => x.Apply(inputSettings, hotspotAnalysisConfigurationMock)) - .Returns(fixedUpSettings); - - // Act - var testSubject = CreateTestSubject(defaultConfig, - inputSettings, - fixup.Object, - hotspotAnalysisConfigurationMock); - - // Assert - fixup.VerifyAll(); - - testSubject.AllPartialRuleKeys.Should().BeEquivalentTo("rule1", "rule2"); - testSubject.ActivePartialRuleKeys.Should().BeEquivalentTo("rule2"); - } - - [TestMethod] - public void ActiveRules_CustomSettingsOverrideDefaults() - { - // Arrange - var defaultConfig = new DummyCFamilyRulesConfig("c") - .AddRule("rule1", isActive: false) - .AddRule("rule2", isActive: true) - .AddRule("rule3", isActive: true); - - var settings = new RulesSettings - { - Rules = new Dictionary - { - // Unknown rules should be ignored - { "x:unknown1", new RuleConfig { Level = RuleLevel.On } }, - - // Turn on a rule that was off (case-insensitive comparison on keys) - { "c:rule1", new RuleConfig { Level = RuleLevel.On } }, - - // Turn off a rule that was on - { "c:rule2", new RuleConfig { Level = RuleLevel.Off} }, - - // Rule key comparison is case-sensitive - { "c:RULE3", new RuleConfig { Level = RuleLevel.Off} }, - - // Settings for other languages should be ignored - { "cpp:rule3", new RuleConfig { Level = RuleLevel.Off } } - } - }; - - // Act - var testSubject = CreateTestSubject(defaultConfig, settings); - - // Assert - testSubject.ActivePartialRuleKeys.Should().BeEquivalentTo("rule1", "rule3"); - } - - [TestMethod] - public void EffectiveSeverity_CustomSettingsOverrideDefaults() - { - // Arrange - var defaultConfig = new DummyCFamilyRulesConfig("c") - .AddRule("rule1", IssueSeverity.Major, isActive: false, null) - .AddRule("rule2", IssueSeverity.Minor, isActive: true, null) - .AddRule("rule3", IssueSeverity.Info, isActive: true, null); - - var settings = new RulesSettings(); - - // Rule 1 - severity not specified -> should use default - settings.Rules["c:rule1"] = new RuleConfig(); - - // Rule 2 - should override default severity - // Rule key comparison should be case-insensitive - settings.Rules["c:RULE2"] = new RuleConfig { Severity = IssueSeverity.Blocker }; - - // Rule 3 for a different language -> should be ignored and the default config used - settings.Rules["cpp:rule3"] = new RuleConfig { Severity = IssueSeverity.Critical }; - - // rule in user settings that isn't in the default config should be ignored - settings.Rules["c:missingRule"] = new RuleConfig { Severity = IssueSeverity.Critical }; - - // Act - var dynamicConfig = CreateTestSubject(defaultConfig, settings); - - // Assert - dynamicConfig.RulesMetadata.Count.Should().Be(3); - dynamicConfig.RulesMetadata["rule1"].DefaultSeverity.Should().Be(IssueSeverity.Major); - dynamicConfig.RulesMetadata["rule2"].DefaultSeverity.Should().Be(IssueSeverity.Blocker); - dynamicConfig.RulesMetadata["rule3"].DefaultSeverity.Should().Be(IssueSeverity.Info); - } - - [TestMethod] - public void Parameters_CustomSettingsOverrideDefaults() - { - // Arrange - var defaultConfig = new DummyCFamilyRulesConfig("c") - .AddRule("rule1", isActive: false, - parameters: new Dictionary - { - { "r1 param1", "r1p1 default" }, - { "r1 param2", "r1p2 default" } - }) - - .AddRule("rule2", isActive: true, - parameters: new Dictionary - { - { "r2 param1", "r2p1 default" }, - { "r2 param2", "r2p2 default" } - }) - .AddRule("rule3", isActive: true, - parameters: new Dictionary - { - { "r3 param1", "r3p1 default" }, - { "r3 param2", "r3p2 default" } - } - ); - - var settings = new RulesSettings - { - Rules = new Dictionary - { - // Rule 1 - no user params -> same as default - - // Rule 2 - all default params overridden - { "c:rule2", new RuleConfig - { - Parameters = new Dictionary - { - { "r2 param1", "r2p1 user"}, - { "r2 param2", "r2p2 user"} - } - } - }, - - // Rule 3 - params merged, with user taking priority - { "c:rule3", new RuleConfig - { - Parameters = new Dictionary - { - { "r3 param1", "r3p1 user"}, - { "r3 param3", "r3p3 user"} - } - } - } - } - }; - - // Act - var dynamicConfig = CreateTestSubject(defaultConfig, settings); - - // Assert - dynamicConfig.RulesParameters.Count.Should().Be(3); - dynamicConfig.RulesParameters["rule1"]["r1 param1"].Should().Be("r1p1 default"); - dynamicConfig.RulesParameters["rule1"]["r1 param2"].Should().Be("r1p2 default"); - dynamicConfig.RulesParameters["rule1"].Count.Should().Be(2); - - dynamicConfig.RulesParameters["rule2"]["r2 param1"].Should().Be("r2p1 user"); - dynamicConfig.RulesParameters["rule2"]["r2 param2"].Should().Be("r2p2 user"); - dynamicConfig.RulesParameters["rule2"].Count.Should().Be(2); - - dynamicConfig.RulesParameters["rule3"]["r3 param1"].Should().Be("r3p1 user"); - dynamicConfig.RulesParameters["rule3"]["r3 param2"].Should().Be("r3p2 default"); - dynamicConfig.RulesParameters["rule3"]["r3 param3"].Should().Be("r3p3 user"); - dynamicConfig.RulesParameters["rule3"].Count.Should().Be(3); - } - - #region Static method tests - - [TestMethod] - public void EffectiveParameters_NullHandling() - { - var nonEmptyParams = new Dictionary { { "p1", "v1" } }; - - // 1. Both null -> null returned - var actual = DynamicCFamilyRulesConfig.GetEffectiveParameters(null, null); - actual.Should().BeNull(); - - // 2. Null default params -> user params returned (not expected in practice, but we don't want to fail if it does) - actual = DynamicCFamilyRulesConfig.GetEffectiveParameters(nonEmptyParams, null); - actual.Should().BeEquivalentTo(nonEmptyParams); - - // 3. Null user params -> default params returned - actual = DynamicCFamilyRulesConfig.GetEffectiveParameters(null, nonEmptyParams); - actual.Should().BeEquivalentTo(nonEmptyParams); - } - - [TestMethod] - public void EffectiveParameters_CustomSettingsOverrideDefaults() - { - // Arrange - var defaultParams = new Dictionary - { - { "param1", "param 1 default" }, - { "param2", "param 2 default" }, - { "param3", "param 3 default" }, // expected - { "param3a", "param 3a default" }, - { "param4", "param 4 default" }, // expected - }; - - var userParams = new Dictionary - { - { "param1", "param 1 user" }, // expected - { "PARAM2", "param 2 user" }, // expected - { "param3a", "param 3a user" }, // expected - not an exact match for param3, should override param3a - // NOTE: params not in the set of default parameters will be included i.e. any arbitrary params set by the user will be included - { "NonDefaultParam", "non-default param value" }, // expected - }; - - // Act - var effectiveParams = DynamicCFamilyRulesConfig.GetEffectiveParameters(defaultParams, userParams); - - // Assert - effectiveParams.Keys.Should().BeEquivalentTo("param1", "param2", "param3", "param3a", "param4", "NonDefaultParam"); - - effectiveParams["param1"].Should().Be("param 1 user"); - effectiveParams["param2"].Should().Be("param 2 user"); - effectiveParams["param3"].Should().Be("param 3 default"); - effectiveParams["param3a"].Should().Be("param 3a user"); - effectiveParams["param4"].Should().Be("param 4 default"); - effectiveParams["NonDefaultParam"].Should().Be("non-default param value"); - } - - #endregion Static method tests - - private static DynamicCFamilyRulesConfig CreateTestSubject(ICFamilyRulesConfig defaultConfig, - RulesSettings customSettings, - IRulesConfigFixup fixup = null, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration = null) - { - fixup ??= new NoOpRulesConfigFixup(); - return new DynamicCFamilyRulesConfig(defaultConfig, customSettings, - connectedModeFeaturesConfiguration ?? Mock.Of(), new TestLogger(), fixup); - } - - private class NoOpRulesConfigFixup : IRulesConfigFixup - { - public RulesSettings Apply(RulesSettings input, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration) => input; - } - } - - internal static class RulesSettingsExtensions - { - public static RulesSettings AddRule(this RulesSettings settings, string ruleKey, RuleLevel level) - { - settings.Rules.Add(ruleKey, new RuleConfig { Level = level }); - return settings; - } - } -} diff --git a/src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorCacheTests.cs b/src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorCacheTests.cs deleted file mode 100644 index 82eec742ee..0000000000 --- a/src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorCacheTests.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class EffectiveRulesConfigCalculatorCacheTests - { - private EffectiveRulesConfigCalculator.RulesConfigCache testSubject; - - [TestInitialize] - public void TestInitialize() - { - testSubject = new EffectiveRulesConfigCalculator.RulesConfigCache(); - } - - [TestMethod] - public void Cache_DifferentSourceConfig_NotFound_AndEntryCleared() - { - var sourceConfig1 = new Mock().Object; - var sourceSettings1 = new RulesSettings(); - var effectiveConfig1 = new Mock().Object; - - testSubject.Add("key1", sourceConfig1, sourceSettings1, effectiveConfig1); - testSubject.CacheCount.Should().Be(1); - - // 1. Search for added item -> found - testSubject.FindConfig("key1", sourceConfig1, sourceSettings1).Should().BeSameAs(effectiveConfig1); - testSubject.CacheCount.Should().Be(1); - - // 2. Different source config -> not found - testSubject.FindConfig("key1", new Mock().Object, sourceSettings1).Should().BeNull(); - testSubject.CacheCount.Should().Be(0); - } - - [TestMethod] - public void Cache_DifferentSourceSettings_NotFound_AndEntryCleared() - { - var sourceConfig1 = new Mock().Object; - var sourceSettings1 = new RulesSettings(); - var effectiveConfig1 = new Mock().Object; - - testSubject.Add("key1", sourceConfig1, sourceSettings1, effectiveConfig1); - testSubject.CacheCount.Should().Be(1); - - // 1. Search for added item -> found - testSubject.FindConfig("key1", sourceConfig1, sourceSettings1).Should().BeSameAs(effectiveConfig1); - testSubject.CacheCount.Should().Be(1); - - // 2. Different source settings -> not found - testSubject.FindConfig("key1", sourceConfig1, new RulesSettings()).Should().BeNull(); - testSubject.CacheCount.Should().Be(0); - } - - [TestMethod] - public void Cache_MultipleEntries() - { - var sourceConfig1 = new Mock().Object; - var sourceConfig2 = new Mock().Object; - - var sourceSettings1 = new RulesSettings(); - var sourceSettings2 = new RulesSettings(); - - var effectiveConfig1 = new Mock().Object; - var effectiveConfig2 = new Mock().Object; - - var testSubject = new EffectiveRulesConfigCalculator.RulesConfigCache(); - - // 1. Empty cache -> cache miss - testSubject.FindConfig("key1", sourceConfig1, sourceSettings1).Should().BeNull(); - - // 2. Add first entry to cache - testSubject.Add("key1", sourceConfig1, sourceSettings1, effectiveConfig1); - testSubject.CacheCount.Should().Be(1); - - // 3. Find second language - not found - testSubject.FindConfig("key2", sourceConfig2, sourceSettings2).Should().BeNull(); - - // 4. Add second entry to cache - testSubject.Add("key2", sourceConfig2, sourceSettings2, effectiveConfig2); - testSubject.CacheCount.Should().Be(2); - - // 5. Check can find both entries - testSubject.FindConfig("key1", sourceConfig1, sourceSettings1).Should().BeSameAs(effectiveConfig1); - testSubject.FindConfig("key2", sourceConfig2, sourceSettings2).Should().BeSameAs(effectiveConfig2); - } - } -} diff --git a/src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorTests.cs b/src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorTests.cs deleted file mode 100644 index 428c1312f3..0000000000 --- a/src/CFamily.UnitTests/Rules/EffectiveRulesConfigCalculatorTests.cs +++ /dev/null @@ -1,181 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class EffectiveRulesConfigCalculatorTests - { - private TestLogger testLogger; - private EffectiveRulesConfigCalculator testSubject; - - [TestInitialize] - public void TestInitialize() - { - testLogger = new TestLogger(); - testSubject = new EffectiveRulesConfigCalculator(Mock.Of(), testLogger); - } - - [TestMethod] - public void GetConfig_NullArguments_Throws() - { - var defaultRulesConfig = CreateWellKnownRulesConfig("language1"); - var customSettings = new RulesSettings(); - - // 1. Language - Action act = () => testSubject.GetEffectiveRulesConfig(null, defaultRulesConfig, customSettings); - act.Should().ThrowExactly().And.ParamName.Should().Be("languageKey"); - - // 2. Default rules config - act = () => testSubject.GetEffectiveRulesConfig("x", null, customSettings); - act.Should().ThrowExactly().And.ParamName.Should().Be("defaultRulesConfig"); - - // 3. Custom settings - act = () => testSubject.GetEffectiveRulesConfig("x", defaultRulesConfig, null); - act.Should().ThrowExactly().And.ParamName.Should().Be("customSettings"); - } - - [TestMethod] - public void GetConfig_NoCustomSettings_DefaultsReturned() - { - // Arrange - var defaultRulesConfig = CreateWellKnownRulesConfig("language1"); - var sourcesSettings = new RulesSettings(); - - // Act - var result = testSubject.GetEffectiveRulesConfig("language1", defaultRulesConfig, sourcesSettings); - - // Assert - result.LanguageKey.Should().Be("language1"); - result.AllPartialRuleKeys.Should().BeEquivalentTo(defaultRulesConfig.AllPartialRuleKeys); - - testLogger.AssertOutputStringExists(Resources.NoCustomRulesSettings); - } - - [DataRow(true, 2)] - [DataRow(false, 1)] - [DataTestMethod] - public void GetConfig_NoCustomSettings_RespectsHotspotConfig(bool analyzeHotspots, int expectedActiveCount) - { - // Arrange - var defaultRulesConfig = new DummyCFamilyRulesConfig("cpp") - .AddRule(RulesConfigFixup.HotspotRulesKeys[0].Split(':')[1], true) - .AddRule("12345678", true); - var sourcesSettings = new RulesSettings(); - var hotspotAnalysisConfigurationMock = new Mock(); - hotspotAnalysisConfigurationMock.Setup(x => x.IsHotspotsAnalysisEnabled()).Returns(analyzeHotspots); - - var testSubject1 = - new EffectiveRulesConfigCalculator(hotspotAnalysisConfigurationMock.Object, testLogger); - - // Act - var result = testSubject1.GetEffectiveRulesConfig("cpp", defaultRulesConfig, sourcesSettings); - - // Assert - result.LanguageKey.Should().Be("cpp"); - result.ActivePartialRuleKeys.Should().HaveCount(expectedActiveCount); - - testLogger.AssertOutputStringExists(Resources.NoCustomRulesSettings); - } - - [TestMethod] - public void GetConfig_RulesInCustomSettings_MergedConfigReturned() - { - // Arrange - var defaultRulesConfig = CreateWellKnownRulesConfig("key"); - var sourcesSettings = new RulesSettings - { - Rules = new Dictionary - { - // Turn an active rule off... - { "key:" + WellKnownPartialRuleKey1_Active, new RuleConfig { Level = RuleLevel.Off } }, - // ... and an inactive rule on - { "key:" + WellKnownPartialRuleKey3_Inactive, new RuleConfig { Level = RuleLevel.On } } - } - }; - - var result = testSubject.GetEffectiveRulesConfig("key", defaultRulesConfig, sourcesSettings); - - result.LanguageKey.Should().Be("key"); - result.AllPartialRuleKeys.Should().BeEquivalentTo(defaultRulesConfig.AllPartialRuleKeys); - result.ActivePartialRuleKeys.Should().BeEquivalentTo(WellKnownPartialRuleKey2_Active, WellKnownPartialRuleKey3_Inactive); - } - - [TestMethod] - public void GetConfig_CachedResultsReturnedIfAvailable() - { - // Arrange - var defaultRulesConfig = CreateWellKnownRulesConfig("key"); - var sourcesSettings = new RulesSettings - { - Rules = new Dictionary - { - { "rule1", new RuleConfig() } - } - }; - - // 1. First call -> new config returned - var result1 = testSubject.GetEffectiveRulesConfig("language1", defaultRulesConfig, sourcesSettings); - - result1.Should().NotBeNull(); - result1.Should().NotBeSameAs(defaultRulesConfig); - testLogger.AssertOutputStringExists(Resources.EffectiveRules_CacheMiss); - - // 2. Second call with same settings -> cache hit - testLogger.Reset(); - var result2 = testSubject.GetEffectiveRulesConfig("language1", defaultRulesConfig, sourcesSettings); - - result2.Should().BeSameAs(result1); - testLogger.AssertOutputStringExists(Resources.EffectiveRules_CacheHit); - - // 3. Call with different key -> cache miss - testLogger.Reset(); - var result3 = testSubject.GetEffectiveRulesConfig("another language", defaultRulesConfig, sourcesSettings); - - result3.Should().NotBeSameAs(result2); - testLogger.AssertOutputStringExists(Resources.EffectiveRules_CacheMiss); - } - - internal const string WellKnownPartialRuleKey1_Active = "rule1"; - internal const string WellKnownPartialRuleKey2_Active = "rule2"; - internal const string WellKnownPartialRuleKey3_Inactive = "rule3"; - - private static ICFamilyRulesConfig CreateWellKnownRulesConfig(string languageKey) - { - var defaultRulesConfig = new DummyCFamilyRulesConfig(languageKey) - .AddRule(WellKnownPartialRuleKey1_Active, IssueSeverity.Blocker, isActive: true, parameters: null, code: null) - .AddRule(WellKnownPartialRuleKey2_Active, IssueSeverity.Major, isActive: true, parameters: null, code: null) - .AddRule(WellKnownPartialRuleKey3_Inactive, IssueSeverity.Minor, isActive: false, parameters: null, code: null); - - return defaultRulesConfig; - } - } -} diff --git a/src/CFamily.UnitTests/Rules/RulesConfigFixupTests.cs b/src/CFamily.UnitTests/Rules/RulesConfigFixupTests.cs deleted file mode 100644 index 0c09ae1b77..0000000000 --- a/src/CFamily.UnitTests/Rules/RulesConfigFixupTests.cs +++ /dev/null @@ -1,214 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Collections.Generic; -using System.Linq; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class RulesConfigFixupTests - { - [TestMethod] - public void Sanity_NoOverlapBetweenExludedAndLegacyRuleKeys() - { - // The logic in the fixup class assumes that there legacy rules keys and - // excluded rules are disjoint sets. Check this is actually the case. - RulesConfigFixup.fullLegacyToNewKeyMap.Keys.Intersect(RulesConfigFixup.ExcludedRulesKeys) - .Should().BeEmpty(); - - RulesConfigFixup.fullLegacyToNewKeyMap.Values.Intersect(RulesConfigFixup.ExcludedRulesKeys) - .Should().BeEmpty(); - } - - [TestMethod] - [DataRow("c:C99CommentUsage", "c:S787")] - [DataRow("cpp:C99CommentUsage", "cpp:S787")] - [DataRow("c:PPBadIncludeForm", "c:S956")] - [DataRow("cpp:PPBadIncludeForm", "cpp:S956")] - [DataRow("CPP:PPBADINCLUDEFORM", "CPP:PPBADINCLUDEFORM")] // replacement is case-sensitive - public void Apply_LegacyRuleKeys_KeysAreTranslated(string inputRuleKey, string expectedRuleKey) - { - var config1 = new RuleConfig { Level = RuleLevel.On, Severity = IssueSeverity.Major }; - var config2 = new RuleConfig { Level = RuleLevel.Off, Severity = IssueSeverity.Minor }; - var config3 = new RuleConfig { Level = RuleLevel.On, Severity = IssueSeverity.Critical }; - - var originalSettings = new RulesSettings - { - Rules = - { - { "any", config1 }, // arbitrary rule key - { inputRuleKey, config2}, - { "cpp:S123", config3 } // non-legacy - } - }; - - var logger = new TestLogger(); - var testSubject = CreateTestSubject(logger); - - var result = testSubject.Apply(originalSettings, CreateHotspotAnalysisConfig()); - - result.Rules["any"].Should().BeSameAs(config1); - result.Rules["cpp:S123"].Should().BeSameAs(config3); - - result.Rules.TryGetValue(expectedRuleKey, out var outputConfig).Should().BeTrue(); - outputConfig.Should().BeSameAs(config2); - - // Not expecting any messages for the non-legacy rule keys - logger.AssertPartialOutputStringDoesNotExist("any"); - logger.AssertPartialOutputStringDoesNotExist("cpp:S123"); - - CheckInstanceIsDifferent(originalSettings, result); - } - - [TestMethod] - [DataRow("c:C99CommentUsage", "c:S787")] - [DataRow("cpp:PPBadIncludeForm", "cpp:S956")] - public void Apply_BothLegacyAndNewRuleKey_LegacyKeyIsRemoved(string legacyKey, string newKey) - { - var legacyKeyConfig = new RuleConfig { Level = RuleLevel.On, Severity = IssueSeverity.Major }; - var newKeyConfig = new RuleConfig { Level = RuleLevel.Off, Severity = IssueSeverity.Minor }; - - var originalSettings = new RulesSettings - { - Rules = - { - { legacyKey, legacyKeyConfig }, - { newKey, newKeyConfig } - } - }; - - var logger = new TestLogger(logToConsole: true); - - var testSubject = CreateTestSubject(logger); - - var result = testSubject.Apply(originalSettings, CreateHotspotAnalysisConfig()); - - result.Rules[newKey].Should().BeSameAs(newKeyConfig); - result.Rules.TryGetValue(legacyKey, out var _).Should().BeFalse(); - - logger.AssertPartialOutputStringExists(legacyKey, newKey); - CheckInstanceIsDifferent(originalSettings, result); - } - - [DataRow(true)] - [DataRow(false)] - [DataTestMethod] - public void Apply_NoCustomRules_ExcludedRulesAreDisabled(bool hotspotsEnabled) - { - var logger = new TestLogger(); - var emptySettings = new RulesSettings(); - var testSubject = CreateTestSubject(logger); - - var result = testSubject.Apply(emptySettings, CreateHotspotAnalysisConfig(hotspotsEnabled)); - - CheckInstanceIsDifferent(emptySettings, result); - emptySettings.Rules.Count.Should().Be(0); // original settings should not have changed - - var excludedKeys = RulesConfigFixup.ExcludedRulesKeys; - - if (!hotspotsEnabled) - { - excludedKeys = excludedKeys.Concat(RulesConfigFixup.HotspotRulesKeys).ToArray(); - } - - result.Rules.Keys.Should().BeEquivalentTo(excludedKeys); - result.Rules.Values.Select(x => x.Level) - .All(x => x == RuleLevel.Off) - .Should().BeTrue(); - - foreach(string excludedKey in excludedKeys) - { - logger.AssertPartialOutputStringExists(excludedKey); - } - } - - [DataRow(true)] - [DataRow(false)] - [DataTestMethod] - public void Apply_CustomRulesAndExcludedRulesExist_CustomRulesAreUnchangedExcludedRulesAreDisabled(bool hotspotsEnabled) - { - // Arrange - var logger = new TestLogger(); - - string excludedKey1 = RulesConfigFixup.ExcludedRulesKeys[0]; - string excludedKey2 = RulesConfigFixup.ExcludedRulesKeys[1]; - string excludedKey3 = RulesConfigFixup.ExcludedRulesKeys[2]; - - var custom = new RulesSettings() - .AddRule("xxx", RuleLevel.On) - .AddRule(excludedKey1, RuleLevel.On) - .AddRule("yyy", RuleLevel.Off) - .AddRule(excludedKey2, RuleLevel.Off) - .AddRule(excludedKey3, RuleLevel.On); - - var testSubject = CreateTestSubject(logger); - - // Act - var result = testSubject.Apply(custom, CreateHotspotAnalysisConfig(hotspotsEnabled)); - - // Assert - CheckInstanceIsDifferent(custom, result); - custom.Rules.Count.Should().Be(5); - - IEnumerable expectedKeys = RulesConfigFixup.ExcludedRulesKeys; - - if (!hotspotsEnabled) - { - expectedKeys = expectedKeys.Concat(RulesConfigFixup.HotspotRulesKeys); - } - - expectedKeys = expectedKeys.Union(new string[] { "xxx", "yyy"}); - - result.Rules.Keys.Should().BeEquivalentTo(expectedKeys); - - // Non-excluded rules should be unchanged - result.Rules["xxx"].Level.Should().Be(RuleLevel.On); - result.Rules["yyy"].Level.Should().Be(RuleLevel.Off); - - // All excluded rules that were in the custom settings should be "Off" - result.Rules[excludedKey1].Level.Should().Be(RuleLevel.Off); - result.Rules[excludedKey2].Level.Should().Be(RuleLevel.Off); - result.Rules[excludedKey3].Level.Should().Be(RuleLevel.Off); - } - - private static IConnectedModeFeaturesConfiguration CreateHotspotAnalysisConfig(bool isEnabled = false) - { - var mock = new Mock(); - mock.Setup(x => x.IsHotspotsAnalysisEnabled()).Returns(isEnabled); - return mock.Object; - } - - private static RulesConfigFixup CreateTestSubject(ILogger logger = null) - => new RulesConfigFixup(logger ?? new TestLogger()); - - // Checks that the changes have been made to a copy of the settings, - // not the original settings. - private static void CheckInstanceIsDifferent(RulesSettings original, RulesSettings modified) => - modified.Should().NotBeSameAs(original); - } -} diff --git a/src/CFamily.UnitTests/Rules/RulesLoaderTest.cs b/src/CFamily.UnitTests/Rules/RulesLoaderTest.cs deleted file mode 100644 index f54a6f3c3d..0000000000 --- a/src/CFamily.UnitTests/Rules/RulesLoaderTest.cs +++ /dev/null @@ -1,154 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.IO; -using FluentAssertions; -using FluentAssertions.Execution; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using static SonarLint.VisualStudio.CFamily.Rules.RulesLoader; - -namespace SonarLint.VisualStudio.CFamily.Rules.UnitTests -{ - [TestClass] - public class RulesLoaderTest - { - [TestMethod] - public void Read_Rules() - { - var rulesLoader = CreateTestSubject(); - // 410 in RulesList.json + 2 in MisraRulesList.json - rulesLoader.ReadRulesList().Should().HaveCount(410 + 2); - } - - [TestMethod] - public void Read_Active_Rules() - { - var rulesLoader = CreateTestSubject(); - rulesLoader.ReadActiveRulesList().Should().HaveCount(255); - } - - [TestMethod] - public void Read_Rules_Params() - { - var rulesLoader = CreateTestSubject(); - rulesLoader.ReadRuleParams("ClassComplexity").Should() - .Contain(new System.Collections.Generic.KeyValuePair("maximumClassComplexityThreshold", "80")); - - rulesLoader.ReadRuleParams("Missing").Should().BeEmpty(); - - // Sanity check, ensure we can read all rules params - foreach (string ruleKey in rulesLoader.ReadRulesList()) - { - rulesLoader.ReadRuleParams(ruleKey).Should().NotBeNull(); - } - } - - [TestMethod] - public void Read_Rules_Metadata() - { - var rulesLoader = CreateTestSubject(); - using (new AssertionScope()) - { - rulesLoader.ReadRuleMetadata("ClassComplexity").Type.Should().Be(IssueType.CodeSmell); - rulesLoader.ReadRuleMetadata("ClassComplexity").DefaultSeverity.Should().Be(IssueSeverity.Critical); - } - - Action act = () => rulesLoader.ReadRuleMetadata("Missing"); - act.Should().ThrowExactly(); - } - - [TestMethod] - public void SonarTypeConverter_CodeSmell() - { - var json = @"{ -title: 'title1', -defaultSeverity: 'CRITICAL', -type: 'CODE_SMELL' -}"; - var ruleMetadata = DeserializeJson(json); - - ruleMetadata.Type.Should().Be(IssueType.CodeSmell); - ruleMetadata.DefaultSeverity.Should().Be(IssueSeverity.Critical); - } - - [TestMethod] - public void SonarTypeConverter_Bug() - { - var json = @"{ -title: 'title1', -defaultSeverity: 'BLOCKER', -type: 'BUG' -}"; - var ruleMetadata = DeserializeJson(json); - - ruleMetadata.Type.Should().Be(IssueType.Bug); - ruleMetadata.DefaultSeverity.Should().Be(IssueSeverity.Blocker); - } - - [TestMethod] - public void SonarTypeConverter_Vulnerability () - { - var json = @"{ -title: 'title1', -defaultSeverity: 'INFO', -type: 'VULNERABILITY' -}"; - var ruleMetadata = DeserializeJson(json); - - ruleMetadata.Type.Should().Be(IssueType.Vulnerability); - ruleMetadata.DefaultSeverity.Should().Be(IssueSeverity.Info); - } - - [TestMethod] - public void SonarTypeConverter_UnknownType_Throws() - { - var json = @"{ -title: 'title1', -defaultSeverity: 'CRITICAL', -type: 'xxx bad type' -}"; - Action act = () => DeserializeJson(json); - - act.Should().ThrowExactly().And.Message.Should().Contain("xxx bad type"); - } - - private static RuleMetadata DeserializeJson(string json) - { - var data = JsonConvert.DeserializeObject(json, new SonarTypeConverter()); - return data; - } - - private static RulesLoader CreateTestSubject() - { - var resourcesPath = Path.Combine( - Path.GetDirectoryName(typeof(RulesLoaderTest).Assembly.Location), - "Rules", "TestResources", "RulesLoader"); - Directory.Exists(resourcesPath).Should().BeTrue($"Test setup error: expected test resources directory does not exist: {resourcesPath}"); - - var rulesLoader = new RulesLoader(resourcesPath); - return rulesLoader; - } - - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity.json b/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity.json deleted file mode 100644 index dc71eddba3..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "title": "Classes should not be too complex", - "type": "CODE_SMELL", - "status": "deprecated", - "remediation": { - "func": "Linear with offset", - "linearDesc": "per complexity point over the threshold", - "linearOffset": "10min", - "linearFactor": "1min" - }, - "tags": [ - - ], - "defaultSeverity": "Critical", - "ruleSpecification": "RSPEC-1311", - "sqKey": "ClassComplexity", - "compatibleLanguages": [ - "CPP", - "OBJC" - ], - "scope": "Main" -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity_params.json b/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity_params.json deleted file mode 100644 index 748e2851e9..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/ClassComplexity_params.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "key": "maximumClassComplexityThreshold", - "description": "Maximum complexity allowed.", - "defaultValue": "80", - "type": "INTEGER" - } -] diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/MisraRulesList.json b/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/MisraRulesList.json deleted file mode 100644 index 579ee574b6..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/MisraRulesList.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "M23_003", - "M23_036" -] diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/RulesList.json b/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/RulesList.json deleted file mode 100644 index 3af1b55fd2..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/RulesList.json +++ /dev/null @@ -1,412 +0,0 @@ -[ - "AssignmentInSubExpression", - "BackJumpWithGoto", - "C99CommentUsage", - "ClassComplexity", - "ClassName", - "CommaAndOrOverloaded", - "CommentMixedStyles", - "CommentRegularExpression", - "CommentedCode", - "ContinueUsage", - "DigraphUsage", - "EllipsisHandlerNotLast", - "ElseIfWithoutElse", - "EmptyCompoundStatement", - "EmptyThrowOutsideHandler", - "EnumPartialInitialization", - "ExceptionInDestructor", - "ExceptionSpecificationUsage", - "FileComplexity", - "FileLoc", - "FunctionComplexity", - "FunctionEllipsis", - "FunctionSinglePointOfExit", - "GlobalMainFunction", - "GlobalNamespaceMembers", - "GotoLabelInNestedBlock", - "GotoUsage", - "IdentifierLongerThan31", - "IncAndDecMixedWithOtherOperators", - "InvalidEscapeSequence", - "LineLength", - "LiteralSuffix", - "LogicalExpressionOperands", - "NamespaceName", - "NarrowAndWideStringConcat", - "NonEmptyCaseWithoutBreak", - "NonReentrantFunction", - "ObsoletePosixFunction", - "OctalConstantAndSequence", - "OneStatementPerLine", - "PPBackslashNotLastCharacter", - "PPBadIncludeForm", - "PPDefineOrUndefFromBlock", - "PPDirectiveIndentation", - "PPErrorDirectiveReached", - "PPIncludeCHeader", - "PPIncludeCstdio", - "PPIncludeCtime", - "PPIncludeNonStandardCharacters", - "PPIncludeNotAtTop", - "PPIncludeSignal", - "PPIncludeStdio", - "PPIncludeTime", - "PPMacroName", - "PPNonStandardInclude", - "PPStringifyAndPastingUsage", - "PPUndefUsage", - "ParsingError", - "S100", - "S1003", - "S1006", - "S1011", - "S1013", - "S1016", - "S1017", - "S1032", - "S1035", - "S1036", - "S1044", - "S1045", - "S106", - "S1065", - "S1066", - "S1067", - "S1068", - "S107", - "S1079", - "S1081", - "S109", - "S110", - "S1103", - "S1110", - "S1116", - "S1117", - "S112", - "S1123", - "S113", - "S1131", - "S1133", - "S1134", - "S1135", - "S1141", - "S1142", - "S1143", - "S1144", - "S1151", - "S116", - "S1163", - "S117", - "S1172", - "S1181", - "S1185", - "S1186", - "S1188", - "S1198", - "S1199", - "S121", - "S1219", - "S1226", - "S1227", - "S1231", - "S1232", - "S1235", - "S1236", - "S1238", - "S1242", - "S1244", - "S1250", - "S1259", - "S1264", - "S1265", - "S127", - "S1270", - "S1271", - "S1291", - "S1301", - "S134", - "S138", - "S139", - "S1448", - "S1451", - "S1479", - "S1481", - "S1578", - "S1642", - "S1656", - "S1669", - "S1679", - "S1699", - "S1704", - "S1705", - "S1706", - "S1707", - "S1708", - "S1709", - "S1712", - "S1749", - "S1750", - "S1751", - "S1760", - "S1761", - "S1762", - "S1763", - "S1764", - "S1767", - "S1768", - "S1771", - "S1772", - "S1773", - "S1774", - "S1820", - "S1821", - "S1831", - "S1836", - "S1854", - "S1862", - "S1871", - "S1874", - "S1878", - "S1986", - "S1990", - "S2095", - "S2123", - "S2156", - "S2190", - "S2193", - "S2209", - "S2216", - "S2234", - "S2259", - "S2275", - "S2323", - "S2324", - "S2335", - "S2342", - "S2343", - "S2387", - "S2393", - "S2479", - "S2486", - "S2583", - "S2589", - "S2637", - "S2665", - "S2668", - "S2681", - "S2737", - "S2738", - "S2753", - "S2754", - "S2757", - "S2761", - "S2806", - "S2807", - "S2808", - "S2813", - "S2815", - "S3222", - "S3229", - "S3230", - "S3231", - "S3252", - "S3261", - "S3358", - "S3400", - "S3432", - "S3457", - "S3458", - "S3468", - "S3469", - "S3470", - "S3471", - "S3485", - "S3486", - "S3490", - "S3491", - "S3516", - "S3518", - "S3519", - "S3520", - "S3522", - "S3529", - "S3539", - "S3540", - "S3541", - "S3542", - "S3543", - "S3548", - "S3549", - "S3562", - "S3574", - "S3576", - "S3584", - "S3588", - "S3590", - "S3608", - "S3609", - "S3624", - "S3626", - "S3628", - "S3630", - "S3636", - "S3642", - "S3646", - "S3654", - "S3656", - "S3657", - "S3659", - "S3685", - "S3687", - "S3689", - "S3691", - "S3692", - "S3696", - "S3698", - "S3708", - "S3715", - "S3719", - "S3726", - "S3728", - "S3729", - "S3730", - "S3731", - "S3732", - "S3743", - "S3744", - "S3776", - "S3805", - "S3806", - "S3807", - "S3923", - "S3935", - "S3936", - "S3949", - "S3972", - "S3973", - "S4143", - "S4144", - "S4263", - "S4334", - "S4962", - "S4963", - "S4997", - "S4998", - "S4999", - "S5000", - "S5008", - "S5018", - "S5019", - "S5020", - "S5025", - "S5028", - "S5180", - "S5184", - "S5205", - "S5213", - "S5259", - "S5261", - "S5262", - "S5263", - "S5265", - "S5266", - "S5267", - "S5269", - "S5270", - "S5271", - "S5272", - "S5273", - "S5274", - "S5275", - "S5277", - "S5278", - "S5279", - "S5280", - "S5281", - "S5283", - "S5293", - "S5297", - "S5298", - "S5302", - "S5303", - "S5305", - "S5306", - "S5307", - "S5308", - "S5309", - "S5318", - "S5319", - "S784", - "S793", - "S802", - "S810", - "S811", - "S812", - "S813", - "S814", - "S819", - "S820", - "S824", - "S831", - "S833", - "S834", - "S835", - "S836", - "S851", - "S853", - "S854", - "S855", - "S856", - "S859", - "S860", - "S864", - "S867", - "S871", - "S872", - "S873", - "S874", - "S876", - "S878", - "S883", - "S886", - "S897", - "S905", - "S920", - "S925", - "S926", - "S929", - "S930", - "S935", - "S936", - "S943", - "S946", - "S950", - "S960", - "S961", - "S966", - "S967", - "S969", - "S977", - "S978", - "S982", - "S984", - "S985", - "S986", - "S989", - "S990", - "S995", - "SideEffectInRightHandSideOfLogical", - "SideEffectInSizeOf", - "SingleDeclarationPerStatement", - "SingleGotoOrBreakPerIteration", - "SizeofSizeof", - "SwitchLabelPlacement", - "SwitchWithoutDefault", - "TabCharacter", - "TrigraphUsage", - "UnaryAndOverloaded", - "Union", - "UnnamedNamespaceInHeader", - "UsingDirective" -] diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/Sonar_way_profile.json b/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/Sonar_way_profile.json deleted file mode 100644 index 47ceb2c1fa..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesLoader/Sonar_way_profile.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "name": "Sonar way", - "ruleKeys": [ - "AssignmentInSubExpression", - "BackJumpWithGoto", - "CommaAndOrOverloaded", - "CommentedCode", - "DigraphUsage", - "EllipsisHandlerNotLast", - "EmptyCompoundStatement", - "EmptyThrowOutsideHandler", - "EnumPartialInitialization", - "ExceptionInDestructor", - "ExceptionSpecificationUsage", - "FunctionEllipsis", - "GlobalMainFunction", - "GotoLabelInNestedBlock", - "InvalidEscapeSequence", - "LiteralSuffix", - "NamespaceName", - "NarrowAndWideStringConcat", - "NonEmptyCaseWithoutBreak", - "NonReentrantFunction", - "ObsoletePosixFunction", - "PPBackslashNotLastCharacter", - "PPBadIncludeForm", - "PPDefineOrUndefFromBlock", - "PPDirectiveIndentation", - "PPIncludeNonStandardCharacters", - "PPIncludeNotAtTop", - "PPNonStandardInclude", - "PPUndefUsage", - "S106", - "S107", - "S110", - "S112", - "S814", - "S819", - "S820", - "S824", - "S836", - "S859", - "S860", - "S872", - "S876", - "S878", - "S897", - "S905", - "S936", - "S961", - "S967", - "S969", - "S977", - "S1003", - "S1006", - "S1011", - "S1017", - "S1035", - "S1036", - "S1044", - "S1045", - "S1065", - "S1066", - "S1068", - "S1079", - "S1081", - "S1103", - "S1110", - "S1116", - "S1117", - "S1123", - "S1133", - "S1134", - "S1135", - "S1141", - "S1143", - "S1144", - "S1163", - "S1172", - "S1181", - "S1185", - "S1186", - "S1198", - "S1199", - "S1219", - "S1231", - "S1232", - "S1235", - "S1236", - "S1238", - "S1242", - "S1250", - "S1264", - "S1265", - "S1301", - "S1448", - "S1479", - "S1481", - "S1656", - "S1669", - "S1679", - "S1699", - "S1709", - "S1751", - "S1760", - "S1761", - "S1763", - "S1764", - "S1767", - "S1768", - "S1771", - "S1820", - "S1831", - "S1836", - "S1854", - "S1862", - "S1871", - "S1874", - "S2095", - "S2123", - "S2190", - "S2193", - "S2209", - "S2216", - "S2234", - "S2259", - "S2275", - "S2323", - "S2387", - "S2479", - "S2486", - "S2583", - "S2589", - "S2637", - "S2665", - "S2668", - "S2681", - "S2737", - "S2738", - "S2753", - "S2754", - "S2757", - "S2761", - "S2808", - "S2813", - "S2815", - "S3229", - "S3230", - "S3231", - "S3252", - "S3261", - "S3358", - "S3432", - "S3457", - "S3458", - "S3468", - "S3469", - "S3470", - "S3485", - "S3486", - "S3491", - "S3516", - "S3518", - "S3519", - "S3520", - "S3522", - "S3529", - "S3539", - "S3540", - "S3541", - "S3543", - "S3548", - "S3574", - "S3576", - "S3584", - "S3588", - "S3590", - "S3608", - "S3624", - "S3626", - "S3636", - "S3646", - "S3656", - "S3657", - "S3659", - "S3689", - "S3691", - "S3692", - "S3696", - "S3698", - "S3708", - "S3726", - "S3728", - "S3729", - "S3730", - "S3731", - "S3732", - "S3743", - "S3744", - "S3776", - "S3805", - "S3806", - "S3807", - "S3923", - "S3935", - "S3936", - "S3949", - "S3972", - "S3973", - "S4143", - "S4144", - "S4263", - "S4963", - "S4997", - "S4998", - "S4999", - "S5000", - "S5008", - "S5019", - "S5020", - "S5028", - "S5180", - "S5184", - "S5205", - "S5213", - "S5259", - "S5261", - "S5262", - "S5263", - "S5265", - "S5266", - "S5267", - "S5269", - "S5271", - "S5272", - "S5273", - "S5274", - "S5275", - "S5277", - "S5278", - "S5279", - "S5280", - "S5281", - "S5283", - "S5293", - "S5297", - "S5308", - "SideEffectInRightHandSideOfLogical", - "SideEffectInSizeOf", - "SingleDeclarationPerStatement", - "SingleGotoOrBreakPerIteration", - "SizeofSizeof", - "SwitchLabelPlacement", - "SwitchWithoutDefault", - "TrigraphUsage", - "UnaryAndOverloaded", - "Union", - "UnnamedNamespaceInHeader" - ] -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1.json deleted file mode 100644 index 6e02ca9c4b..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "title": "All ActiveWithParams 1 title", - "type": "CODE_SMELL", - "status": "deprecated", - "remediation": { - "func": "Linear with offset", - "linearDesc": "per complexity point over the threshold", - "linearOffset": "10min", - "linearFactor": "1min" - }, - "tags": [ - - ], - "defaultSeverity": "Critical", - "ruleSpecification": "RSPEC-1311", - "sqKey": "All_ActiveWithParams_1", - "compatibleLanguages": [ - "C", - "CPP", - "OBJC" - ], - "scope": "Main" -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1_params.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1_params.json deleted file mode 100644 index 748e2851e9..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_ActiveWithParams_1_params.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "key": "maximumClassComplexityThreshold", - "description": "Maximum complexity allowed.", - "defaultValue": "80", - "type": "INTEGER" - } -] diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Active_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Active_1.json deleted file mode 100644 index 45e48aab60..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Active_1.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "title": "All_Active_1 title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "convention", - "based-on-misra" - ], - "standards": [ - "MISRA C 2004" - ], - "defaultSeverity": "Minor", - "ruleSpecification": "RSPEC-787", - "sqKey": "C99CommentUsage", - "compatibleLanguages": [ - "C", - "CPP", - "OBJC" - ], - "scope": "Main" -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Inactive_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Inactive_1.json deleted file mode 100644 index ffc4d88d86..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/All_Inactive_1.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "title": "All Inactive", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "convention", - "based-on-misra" - ], - "standards": [ - "MISRA C 2004" - ], - "defaultSeverity": "Minor", - "ruleSpecification": "RSPEC-787", - "sqKey": "All_Inactive_1", - "compatibleLanguages": [ - "C", - "CPP", - "OBJC" - ], - "scope": "Main" -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Active_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Active_1.json deleted file mode 100644 index ff99855ff0..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Active_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "C Active 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "C_Active_1", - "compatibleLanguages": [ - "C" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_1.json deleted file mode 100644 index 0b6f437cca..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "C Inactive 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "C_Inactive_1", - "compatibleLanguages": [ - "C" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_2.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_2.json deleted file mode 100644 index b00837cdce..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Inactive_2.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "C Inactive 2 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "C_Inactive_2", - "compatibleLanguages": [ - "C" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Active_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Active_1.json deleted file mode 100644 index 7dea2a6573..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Active_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "C MISRA Active 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "C_Active_1", - "compatibleLanguages": [ - "C" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Inactive_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Inactive_1.json deleted file mode 100644 index e000fd9581..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/C_Misra_Inactive_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "C MISRA Inactive 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "C_Inactive_1", - "compatibleLanguages": [ - "C" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_1.json deleted file mode 100644 index 0f339961b7..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "Cpp Active 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "Cpp_Active_1", - "compatibleLanguages": [ - "Cpp" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_2.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_2.json deleted file mode 100644 index d9ac096a97..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Active_2.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "Cpp Active 2 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "Cpp_Active_2", - "compatibleLanguages": [ - "Cpp" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Inactive_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Inactive_1.json deleted file mode 100644 index b9c501ebfa..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Inactive_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "Cpp Inactive 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "Cpp_Inactive_1", - "compatibleLanguages": [ - "CPP" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Active_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Active_1.json deleted file mode 100644 index d77a1b71ff..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Active_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "Cpp MISRA Active 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "Cpp_Active_1", - "compatibleLanguages": [ - "Cpp" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Inactive_1.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Inactive_1.json deleted file mode 100644 index 32795a7612..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Cpp_Misra_Inactive_1.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "title": "Cpp MISRA Inactive 1 Title", - "type": "CODE_SMELL", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "cwe", - "based-on-misra", - "cert", - "suspicious" - ], - "standards": [ - "MISRA C 2004", - "MISRA C 2012", - "CWE", - "CERT" - ], - "defaultSeverity": "Major", - "ruleSpecification": "RSPEC-1121", - "sqKey": "Cpp_Inactive_1", - "compatibleLanguages": [ - "CPP" - ], - "scope": "All", - "securityStandards": { - "CWE": [ - 481 - ] - } -} diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/MisraRulesList.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/MisraRulesList.json deleted file mode 100644 index 2af02c7f75..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/MisraRulesList.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - "C_Misra_Active_1", - "C_Misra_Inactive_1", - "Cpp_Misra_Active_1", - "Cpp_Misra_Inactive_1" -] diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/RulesList.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/RulesList.json deleted file mode 100644 index 3a45f26fab..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/RulesList.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - "All_Active_1", - "All_ActiveWithParams_1", - "All_Inactive_1", - "C_Active_1", - "C_Inactive_1", - "C_Inactive_2", - "Cpp_Active_1", - "Cpp_Active_2", - "Cpp_Inactive_1" -] \ No newline at end of file diff --git a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Sonar_way_profile.json b/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Sonar_way_profile.json deleted file mode 100644 index abdf165c3d..0000000000 --- a/src/CFamily.UnitTests/Rules/TestResources/RulesMetadataCache/Sonar_way_profile.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "Sonar way", - "ruleKeys": [ - "All_Active_1", - "All_ActiveWithParams_1", - "C_Active_1", - "Cpp_Active_1", - "Cpp_Active_2", - "C_Misra_Active_1", - "Cpp_Misra_Active_1" - ] -} \ No newline at end of file diff --git a/src/CFamily.UnitTests/Subprocess/MessageHandlerTests.cs b/src/CFamily.UnitTests/Subprocess/MessageHandlerTests.cs deleted file mode 100644 index 0acf246c84..0000000000 --- a/src/CFamily.UnitTests/Subprocess/MessageHandlerTests.cs +++ /dev/null @@ -1,264 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.CFamily.Analysis; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.Core.Analysis; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.CFamily.SubProcess.UnitTests -{ - [TestClass] - public class MessageHandlerTests - { - [TestMethod] - public void TestIsIssueForActiveRule() - { - var rulesConfig = new DummyCFamilyRulesConfig("any") - .AddRule("rule1", isActive: true) - .AddRule("rule2", isActive: false); - - // 1. Match - active - var message = new Message("rule1", "filename", 0, 0, 0, 0, "msg", false, null, Array.Empty()); - MessageHandler.IsIssueForActiveRule(message, rulesConfig).Should().BeTrue(); - - // 2. Match - not active - message = new Message("rule2", "filename", 0, 0, 0, 0, "msg", false, null, Array.Empty()); - MessageHandler.IsIssueForActiveRule(message, rulesConfig).Should().BeFalse(); - - // 3. No match - case-sensitivity - message = new Message("RULE1", "filename", 0, 0, 0, 0, "msg", false, null, Array.Empty()); - MessageHandler.IsIssueForActiveRule(message, rulesConfig).Should().BeFalse(); - - // 4. No match - message = new Message("xxx", "filename", 0, 0, 0, 0, "msg", false, null, Array.Empty()); - MessageHandler.IsIssueForActiveRule(message, rulesConfig).Should().BeFalse(); - } - - [TestMethod] - public void HandleMessage_NoMessages_AnalysisSucceeds() - { - var context = new MessageHandlerTestContext(); - - var testSubject = context.CreateTestSubject(); - - testSubject.AnalysisSucceeded.Should().BeTrue(); - } - - [TestMethod] - public void HandleMessage_IssueProcessing_InactiveRulesAreIgnored_ActiveRulesAreNotIgnored() - { - const string fileName = "c:\\data\\aaa\\bbb\\file.txt"; - var inactiveRuleMessage = CreateMessage("inactiveRule", fileName); - var activeRuleMessage = CreateMessage("activeRule", fileName); - - var context = new MessageHandlerTestContext() - .SetRequestFilePath(fileName) - .AddRule("inactiveRule", isActive: false) - .AddRule("activeRule", isActive: true); - - var convertedActiveMessage = Mock.Of(); - context.IssueConverter - .Setup(x => x.Convert(activeRuleMessage, context.AnalysisLanguageKey, context.RulesConfig)) - .Returns(convertedActiveMessage); - - var testSubject = context.CreateTestSubject(); - - // Stream the inactive rule message to the analyzer - // - should not be passed to the consumer or converted - testSubject.HandleMessage(inactiveRuleMessage); - - context.AssertNoIssuesProcessed(); - - // Now stream an active rule message - testSubject.HandleMessage(activeRuleMessage); - - testSubject.AnalysisSucceeded.Should().BeTrue(); - testSubject.IssueCount.Should().Be(1); - context.IssueConverter.VerifyAll(); - - var suppliedIssues = (IEnumerable)context.IssueConsumer.Invocations[0].Arguments[1]; - suppliedIssues.Count().Should().Be(1); - suppliedIssues.First().Should().Be(convertedActiveMessage); - } - - [TestMethod] - [DataRow("c:\\Analyzedfile.txt")] // exact match - [DataRow("C:\\ANALYZEDFILE.TXT")] // different case - [DataRow("C:\\AAA\\..\\ANALYZEDFILE.TXT")] // logically the same file path - public void HandleMessage_IssuesForAnalyzedFileAreNotIgnored(string fileNameInMessage) - { - const string analyzedFile = "c:\\Analyzedfile.txt"; - var analyzedFileMessage = CreateMessage("activeRule", fileNameInMessage); - - var context = new MessageHandlerTestContext() - .SetRequestFilePath(analyzedFile) - .AddRule("activeRule", isActive: true); - - var testSubject = context.CreateTestSubject(); - - // Process the message - testSubject.HandleMessage(analyzedFileMessage); - - testSubject.AnalysisSucceeded.Should().BeTrue(); - context.IssueConverter.Invocations.Count.Should().Be(1); - context.IssueConsumer.Invocations.Count.Should().Be(1); - - context.IssueConsumer.Verify(x => x.SetIssues(analyzedFile, It.IsAny>())); - } - - [TestMethod] - [DataRow("")] - [DataRow("another file")] - [DataRow("D:\\Analyzedfile.txt")] // correct file name, wrong drive - public void HandleMessage_IssuesForOtherFilesAreIgnored(string messageFileName) - { - const string analyzedFile = "c:\\Analyzedfile.txt"; - var otherFileMessage = CreateMessage("activeRule", messageFileName); - - var context = new MessageHandlerTestContext() - .SetRequestFilePath(analyzedFile) - .AddRule("activeRule", isActive: true); - - var testSubject = context.CreateTestSubject(); - - // Process the message - testSubject.HandleMessage(otherFileMessage); - - testSubject.AnalysisSucceeded.Should().BeTrue(); // analysis still succeeded, even though no issues reported. - context.AssertNoIssuesProcessed(); - } - - [TestMethod] - [DataRow("internal.InvalidInput", "MsgHandler_ReportInvalidInput")] - [DataRow("internal.UnexpectedFailure", "MsgHandler_ReportUnexpectedFailure")] - [DataRow("internal.UnsupportedConfig", "MsgHandler_ReportUnsupportedConfiguration")] - public void HandleMessage_InternalErrorMessage_IsReportedAndAnalysisFails(string internalRuleKey, string expectedResourceMessageName) - { - // Note: this test assumes that all of the internal rule error messages have a single placeholder - // into which the message text is inserted. - string logMessageFormat = CFamilyStrings.ResourceManager.GetString(expectedResourceMessageName); - var expectedLogMessage = string.Format(logMessageFormat, "XXX internal error XXX"); - - var internalMessage = CreateMessage(internalRuleKey, text: "XXX internal error XXX"); - - var context = new MessageHandlerTestContext() - .AddRule("S123", isActive: true); - var testSubject = context.CreateTestSubject(); - - // Act - testSubject.HandleMessage(internalMessage); - - testSubject.AnalysisSucceeded.Should().BeFalse(); - context.Logger.AssertOutputStringExists(expectedLogMessage); - context.AssertNoIssuesProcessed(); - } - - [TestMethod] - [DataRow("internal.fileDependency")] // real property - [DataRow("internal.something")] // fake property - [DataRow("internal.InvalidInputtttt")] // testing that it takes any starts with - public void HandleMessage_UnknownInternalRules_IsIgnored(string ruleId) - { - var internalMessage = CreateMessage(ruleId, text: "c:\\file.txt"); - - var context = new MessageHandlerTestContext() - // The message file name matches the file being analyzed, but should be ignored anyway - // because of the rule key - .SetRequestFilePath("c:\\file.txt"); - - var testSubject = context.CreateTestSubject(); - - // Act - testSubject.HandleMessage(internalMessage); - - testSubject.AnalysisSucceeded.Should().BeTrue(); - context.Logger.AssertNoOutputMessages(); - context.AssertNoIssuesProcessed(); - } - - private static Message CreateMessage(string ruleId, string fileName = "any file", string text = "any text") => - new(ruleId, fileName, -1, -1, -1, -1, text, false, null, Array.Empty()); - - - private class MessageHandlerTestContext - { - public Mock IssueConsumer { get; } = new Mock(); - public Mock IssueConverter { get; } = new Mock(); - public TestLogger Logger { get; } = new TestLogger(logToConsole: true); - - private string requestFilePath = "any.txt"; - private const string languageKey = "c"; - - public string AnalysisLanguageKey { get; } = languageKey; - - public DummyCFamilyRulesConfig RulesConfig { get; } = new DummyCFamilyRulesConfig(languageKey); - - public MessageHandler TestSubject { get; set; } - - public MessageHandlerTestContext SetRequestFilePath (string fileToAnalyze) - { - requestFilePath = fileToAnalyze; - return this; - } - - public MessageHandlerTestContext AddRule(string ruleKey, bool isActive) - { - RulesConfig.AddRule(ruleKey, isActive); - return this; - } - - public MessageHandler CreateTestSubject() - { - if (TestSubject != null) - { - throw new InvalidOperationException("Test setup error: TestSubject has already been created"); - } - - var request = CreateRequest(requestFilePath, AnalysisLanguageKey, RulesConfig); - - TestSubject = new MessageHandler(request, IssueConsumer.Object, IssueConverter.Object, Logger); - return TestSubject; - } - - public void AssertNoIssuesProcessed() - { - TestSubject.IssueCount.Should().Be(0); - IssueConverter.Invocations.Count.Should().Be(0); - IssueConsumer.Invocations.Count.Should().Be(0); - } - - private static IRequest CreateRequest(string file = null, string language = null, ICFamilyRulesConfig rulesConfiguration = null) - { - var request = new Mock(); - var context = new RequestContext(language, rulesConfiguration, file, null, null, CFamilyShared.IsHeaderFileExtension(file)); - request.SetupGet(x => x.Context).Returns(context); - return request.Object; - } - } - } -} diff --git a/src/CFamily.UnitTests/Subprocess/NoOpMessageHandlerTests.cs b/src/CFamily.UnitTests/Subprocess/NoOpMessageHandlerTests.cs deleted file mode 100644 index a2511c7e44..0000000000 --- a/src/CFamily.UnitTests/Subprocess/NoOpMessageHandlerTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace SonarLint.VisualStudio.CFamily.SubProcess.UnitTests -{ - [TestClass] - public class NoOpMessageHandlerTests - { - [TestMethod] - public void Singleton() - { - var instance1 = NoOpMessageHandler.Instance; - var instance2 = NoOpMessageHandler.Instance; - - instance1.Should().BeSameAs(instance2); - } - - [TestMethod] - public void AnalysisSucceeded_IsTrue() - { - NoOpMessageHandler.Instance.AnalysisSucceeded.Should().BeTrue(); - } - - [TestMethod] - public void HandleOutput_DoesNothing() - { - var message = new Message("key", "file", 1, 2, 3, 4, "text", false, null, Array.Empty()); - - var testSubject = new NoOpMessageHandler(); - - testSubject.IssueCount.Should().Be(0); // check initial state - - testSubject.HandleMessage(message); - testSubject.HandleMessage(null); - - testSubject.IssueCount.Should().Be(0); - } - } -} diff --git a/src/CFamily.UnitTests/Subprocess/ProcessRunnerTests.cs b/src/CFamily.UnitTests/Subprocess/ProcessRunnerTests.cs deleted file mode 100644 index f005e32dfb..0000000000 --- a/src/CFamily.UnitTests/Subprocess/ProcessRunnerTests.cs +++ /dev/null @@ -1,528 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.IO; -using SonarLint.VisualStudio.CFamily.Helpers.UnitTests; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.CFamily.SubProcess.UnitTests -{ - [TestClass] - public class ProcessRunnerTests - { - public TestContext TestContext { get; set; } - - [TestMethod] - public void Execute_WhenRunnerArgsIsNull_ThrowsArgumentNullException() - { - // Arrange - Action action = () => new ProcessRunner(new ConfigurableSonarLintSettings(), new TestLogger()).Execute(null); - - // Act & Assert - action.Should().ThrowExactly().And.ParamName.Should().Be("runnerArgs"); - } - - [TestMethod] - public void ProcRunner_ExecutionFailed() - { - // Arrange - var exeName = WriteBatchFileForTest(TestContext, "exit -2"); - - var logger = new TestLogger(); - var args = new ProcessRunnerArguments(exeName, true); - var runner = CreateProcessRunner(logger); - - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(-2, "Unexpected exit code"); - } - - [TestMethod] - public void ProcRunner_ExecutionSucceeded() - { - // Arrange - var exeName = WriteBatchFileForTest(TestContext, -@"@echo Hello world -@echo xxx yyy -@echo Testing 1,2,3...>&2 -"); - - var logger = new TestLogger(); - var args = new ProcessRunnerArguments(exeName, true); - var output = ""; - args.HandleOutputStream = reader => output = reader.ReadToEnd(); - var runner = CreateProcessRunner(logger); - - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - - output.Should().Contain("Hello world"); - output.Should().NotContain("Testing 1,2,3..."); - } - - [TestMethod] - public void ProcRunner_PassesEnvVariables() - { - // Arrange - var logger = new TestLogger(); - var runner = CreateProcessRunner(logger); - - var exeName = WriteBatchFileForTest(TestContext, -@"echo %PROCESS_VAR% -@echo %PROCESS_VAR2% -@echo %PROCESS_VAR3% -"); - var envVariables = new Dictionary() { - { "PROCESS_VAR", "PROCESS_VAR value" }, - { "PROCESS_VAR2", "PROCESS_VAR2 value" }, - { "PROCESS_VAR3", "PROCESS_VAR3 value" } }; - - var args = new ProcessRunnerArguments(exeName, true) - { - EnvironmentVariables = envVariables - }; - var output = ""; - args.HandleOutputStream = reader => output = reader.ReadToEnd(); - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - - output.Should().Contain("PROCESS_VAR value"); - output.Should().Contain("PROCESS_VAR2 value"); - output.Should().Contain("PROCESS_VAR3 value"); - } - - [TestMethod] - public void ProcRunner_PassesEnvVariables_OverrideExisting() - { - // Tests that existing environment variables will be overwritten successfully - - // Arrange - var logger = new TestLogger(); - var runner = CreateProcessRunner(logger); - var output = ""; - - try - { - // It's possible the user won't be have permissions to set machine level variables - // (e.g. when running on a build agent). Carry on with testing the other variables. - SafeSetEnvironmentVariable("proc.runner.test.machine", "existing machine value", EnvironmentVariableTarget.Machine, logger); - Environment.SetEnvironmentVariable("proc.runner.test.process", "existing process value", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("proc.runner.test.user", "existing user value", EnvironmentVariableTarget.User); - - var exeName = WriteBatchFileForTest(TestContext, -@"@echo file: %proc.runner.test.machine% -@echo file: %proc.runner.test.process% -@echo file: %proc.runner.test.user% -"); - - var envVariables = new Dictionary() { - { "proc.runner.test.machine", "machine override" }, - { "proc.runner.test.process", "process override" }, - { "proc.runner.test.user", "user override" } }; - - var args = new ProcessRunnerArguments(exeName, true) - { - EnvironmentVariables = envVariables, - HandleOutputStream = reader => - { - output = reader.ReadToEnd(); - } - }; - - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - } - finally - { - SafeSetEnvironmentVariable("proc.runner.test.machine", null, EnvironmentVariableTarget.Machine, logger); - Environment.SetEnvironmentVariable("proc.runner.test.process", null, EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("proc.runner.test.user", null, EnvironmentVariableTarget.User); - } - - // Check the child process used expected values - output.Should().Contain("file: machine override"); - output.Should().Contain("file: process override"); - output.Should().Contain("file: user override"); - - // Check the runner reported it was overwriting existing variables - // Note: the existing non-process values won't be visible to the child process - // unless they were set *before* the test host launched, which won't be the case. - logger.AssertSingleDebugMessageExists("proc.runner.test.process", "existing process value", "process override"); - } - - [TestMethod] - public void ProcRunner_MissingExe() - { - // Tests attempting to launch a non-existent exe - - // Arrange - var logger = new TestLogger(); - var args = new ProcessRunnerArguments("missingExe.foo", false); - var runner = CreateProcessRunner(logger); - - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(ProcessRunner.ErrorCode, "Unexpected exit code"); - logger.AssertSingleErrorExists("missingExe.foo"); - } - - [TestMethod] - public void ProcRunner_ArgumentQuoting() - { - // Checks arguments passed to the child process are correctly quoted - - // Arrange - var testDir = CreateTestSpecificFolder(TestContext); - // Create a dummy exe that will produce a log file showing any input args - var exeName = DummyExeHelper.CreateDummyExe(testDir, 0); - - var logger = new TestLogger(); - var args = new ProcessRunnerArguments(exeName, false); - - var expected = new[] { - "unquoted", - "\"quoted\"", - "\"quoted with spaces\"", - "/test:\"quoted arg\"", - "unquoted with spaces", - "quote in \"the middle", - "quotes \"& ampersands", - "\"multiple \"\"\" quotes \" ", - "trailing backslash \\", - "all special chars: \\ / : * ? \" < > | %", - "injection \" > foo.txt", - "injection \" & echo haha", - "double escaping \\\" > foo.txt" - }; - - args.CmdLineArgs = expected; - - var runner = CreateProcessRunner(logger); - - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - - // Check that the public and private arguments are passed to the child process - var exeLogFile = DummyExeHelper.AssertDummyExeLogExists(testDir, TestContext); - DummyExeHelper.AssertExpectedLogContents(exeLogFile, expected); - } - - [TestMethod] - public void ProcRunner_ArgumentQuotingForwardedByBatchScript() - { - // Checks arguments passed to a batch script which itself passes them on are correctly escaped - - // Arrange - var testDir = CreateTestSpecificFolder(TestContext); - // Create a dummy exe that will produce a log file showing any input args - var exeName = DummyExeHelper.CreateDummyExe(testDir, 0); - - var batchName = WriteBatchFileForTest(TestContext, "\"" + exeName + "\" %*"); - - var logger = new TestLogger(); - var args = new ProcessRunnerArguments(batchName, true); - - var expected = new[] { - "unquoted", - "\"quoted\"", - "\"quoted with spaces\"", - "/test:\"quoted arg\"", - "unquoted with spaces", - "quote in \"the middle", - "quotes \"& ampersands", - "\"multiple \"\"\" quotes \" ", - "trailing backslash \\", - "all special chars: \\ / : * ? \" < > | %", - "injection \" > foo.txt", - "injection \" & echo haha", - "double escaping \\\" > foo.txt" - }; - - args.CmdLineArgs = expected; - - var runner = CreateProcessRunner(logger); - - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - - // Check that the public and private arguments are passed to the child process - var exeLogFile = DummyExeHelper.AssertDummyExeLogExists(testDir, TestContext); - DummyExeHelper.AssertExpectedLogContents(exeLogFile, expected); - } - - [TestMethod] - [WorkItem(126)] // Exclude secrets from log data: http://jira.sonarsource.com/browse/SONARMSBRU-126 - public void ProcRunner_DoNotLogSensitiveData() - { - // Arrange - var testDir = CreateTestSpecificFolder(TestContext); - // Create a dummy exe that will produce a log file showing any input args - var exeName = DummyExeHelper.CreateDummyExe(testDir, 0); - - var logger = new TestLogger(); - - // Public args - should appear in the log - var publicArgs = new string[] - { - "public1", - "public2", - "/d:sonar.projectKey=my.key" - }; - - var sensitiveArgs = new string[] { - // Public args - should appear in the log - "public1", "public2", "/dmy.key=value", - - // Sensitive args - should not appear in the log - "/d:sonar.password=secret data password", - "/d:sonar.login=secret data login", - "/d:sonar.jdbc.password=secret data db password", - "/d:sonar.jdbc.username=secret data db user name", - - // Sensitive args - different cases -> exclude to be on the safe side - "/d:SONAR.jdbc.password=secret data db password upper", - "/d:sonar.PASSWORD=secret data password upper", - - // Sensitive args - parameter format is slightly incorrect -> exclude to be on the safe side - "/dsonar.login =secret data key typo", - "sonar.password=secret data password typo" - }; - - var allArgs = sensitiveArgs.Union(publicArgs).ToArray(); - - var runnerArgs = new ProcessRunnerArguments(exeName, false) - { - CmdLineArgs = allArgs, - - // Specify the arguments we consider to be sensitive. - // Note: this is a change from the S4MSB which has a hard-coded set of sensitive keys. - SensitivePropertyKeys = new string[] - { - "sonar.password", "sonar.login", "sonar.jdbc.password", "sonar.jdbc.username" - } - }; - - var runner = CreateProcessRunner(logger); - - // Act - runner.Execute(runnerArgs); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - - // Check public arguments are logged but private ones are not - foreach (var arg in publicArgs) - { - logger.AssertSingleDebugMessageExists(arg); - } - - logger.AssertSingleDebugMessageExists(""); - AssertTextDoesNotAppearInLog("secret", logger); - - // Check that the public and private arguments are passed to the child process - var exeLogFile = DummyExeHelper.AssertDummyExeLogExists(testDir, TestContext); - DummyExeHelper.AssertExpectedLogContents(exeLogFile, allArgs); - } - - [TestMethod] - public void Execute_CancellationTokenIsAlreadyCancelled_ProcessNotExecuted() - { - var exeName = WriteBatchFileForTest(TestContext, - @"@echo Hello world -xxx yyy -@echo Testing 1,2,3...>&2 -"); - - var logger = new TestLogger(); - var args = new ProcessRunnerArguments(exeName, true); - var runner = CreateProcessRunner(logger); - - args.CancellationToken = new CancellationToken(true); - var output = ""; - args.HandleOutputStream = reader => output = reader.ReadToEnd(); - // Act - runner.Execute(args); - - // Assert - runner.ExitCode.Should().Be(0, "Unexpected exit code"); - - output.Should().NotContain("Hello world"); - } - - [TestMethod] - public void Execute_CancellationTokenCancelledMidway_CancelledDuringWritingRequest_ProcessKilled() - { - var exeName = WriteBatchFileForTest(TestContext, @" -echo started! -:again - set /p arg= - echo %arg% - if %arg% == END (goto finished) - goto again -:finished - echo done!"); - - using var processCancellationTokenSource = new CancellationTokenSource(); - var logger = new TestLogger(true, true); - var args = new ProcessRunnerArguments(exeName, true) - { - CancellationToken = processCancellationTokenSource.Token - }; - var output = ""; - args.HandleOutputStream = reader => output = reader.ReadToEnd(); - args.HandleInputStream = writer => - { - writer.WriteLine("dummy"); - Thread.Sleep(2500); - writer.WriteLine("END"); - }; - - var runner = CreateProcessRunner(logger); - var processTask = Task.Run(() => { runner.Execute(args); }); - - processCancellationTokenSource.CancelAfter(500); - - Task.WaitAll(new[] { processTask }, TimeSpan.FromSeconds(15)); - - runner.ExitCode.Should().Be(-1, "Unexpected exit code"); - processCancellationTokenSource.IsCancellationRequested.Should().BeTrue(); - output.Should().Contain("started!"); - output.Should().Contain("dummy"); - output.Should().NotContain("done!"); - } - - [TestMethod] - public void Execute_CancellationTokenCancelledAfterProcessAlreadyFinished_DoesNotThrow() - { - var exeName = WriteBatchFileForTest(TestContext, - @"@echo Hello world -xxx yyy -@echo Testing 1,2,3...>&2 -"); - - var logger = new TestLogger(); - var args = new ProcessRunnerArguments(exeName, true); - var runner = CreateProcessRunner(logger); - - var cancellationTokenSource = new CancellationTokenSource(); - args.CancellationToken = cancellationTokenSource.Token; - - runner.Execute(args); - - Action act = () => cancellationTokenSource.Cancel(false); - - act.Should().NotThrow(); - } - - #region Private methods - - private static ProcessRunner CreateProcessRunner(ILogger logger) - { - return new ProcessRunner(new ConfigurableSonarLintSettings(), logger); - } - - private static void SafeSetEnvironmentVariable(string key, string value, EnvironmentVariableTarget target, ILogger logger) - { - try - { - Environment.SetEnvironmentVariable(key, value, target); - } - catch (System.Security.SecurityException) - { - logger.WriteLine("TEST SETUP ERROR: user running the test doesn't have the permissions to set the environment variable. Key: {0}, value: {1}, target: {2}", - key, value, target); - } - } - - private static void AssertTextDoesNotAppearInLog(string text, TestLogger logger) - { - var matches = logger.OutputStrings.Where(m => m.Contains(text)); - matches.Should().BeEmpty($"Not expecting the text to appear in the log output: {text}"); - } - - /// - /// Creates a batch file with the name of the current test - /// - /// Returns the full file name of the new file - private static string WriteBatchFileForTest(TestContext context, string content) - { - var testPath = CreateTestSpecificFolder(context); - var fileName = Path.Combine(testPath, "test.bat"); - File.Exists(fileName).Should().BeFalse("Not expecting a batch file to already exist: {0}", fileName); - File.WriteAllText(fileName, content); - return fileName; - } - - private static string CreateTestSpecificFolder(TestContext testContext) - { - var testPath = Path.Combine(testContext.DeploymentDirectory, testContext.TestName); - - if (!Directory.Exists(testPath)) - { - Directory.CreateDirectory(testPath); - } - return testPath; - } - - #endregion Private methods - } - - internal static class LoggerExtensions - { - public static void AssertSingleErrorExists(this TestLogger logger, string expected) - { - AssertSinglePartialMessageExists(logger, CFamilyStrings.MSG_Prefix_ERROR, expected); - } - - public static void AssertSingleDebugMessageExists(this TestLogger logger, params string[] expected) - { - var messageParts = new List(expected); - messageParts.Add(CFamilyStrings.MSG_Prefix_DEBUG); - - AssertSinglePartialMessageExists(logger, messageParts.ToArray()); - } - - private static void AssertSinglePartialMessageExists(this TestLogger logger, params string[] expected) - { - var matches = logger.OutputStrings.Where(m => expected.All(e => m.Contains(e))); - matches.Should().ContainSingle("More than one message contains the expected strings: {0}", string.Join(",", expected)); - } - } -} diff --git a/src/CFamily.UnitTests/Subprocess/ProtocolTest.cs b/src/CFamily.UnitTests/Subprocess/ProtocolTest.cs deleted file mode 100644 index e2c0ce4066..0000000000 --- a/src/CFamily.UnitTests/Subprocess/ProtocolTest.cs +++ /dev/null @@ -1,569 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.IO; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace SonarLint.VisualStudio.CFamily.SubProcess.UnitTests -{ - [TestClass] - public class ProtocolTest - { - - #region Protocol-level reading/writing tests - - [TestMethod] - public void Read_Empty_Response() - { - var response = CallProtocolRead(MockEmptyResponse()); - - response.Messages.Length.Should().Be(0); - } - - [TestMethod] - public void Read_RequestHasFileName_ReturnedMessageHasNoFileName_MessageIsNotIgnored() - { - const string returnedIssueFileName = ""; - - var response = CallProtocolRead(MockResponse(returnedIssueFileName)); - - response.Messages.Length.Should().Be(1); - response.Messages[0].Filename.Should().Be(string.Empty); - } - - [TestMethod] - public void Read_Response() - { - var response = CallProtocolRead(MockResponse()); - - response.Messages.Length.Should().Be(1); - response.Messages[0].RuleKey.Should().Be("ruleKey"); - response.Messages[0].Line.Should().Be(10); - response.Messages[0].Column.Should().Be(11); - response.Messages[0].EndLine.Should().Be(12); - response.Messages[0].EndColumn.Should().Be(13); - response.Messages[0].Text.Should().Be("Issue message"); - response.Messages[0].PartsMakeFlow.Should().Be(true); - response.Messages[0].Parts.Length.Should().Be(1); - } - - [TestMethod] - public void Response_WithDataFlow_DataFlowIgnored() - { - var response = CallProtocolRead(MockResponseWithDataFlow()); - - response.Messages.Length.Should().Be(1); - response.Messages[0].RuleKey.Should().Be("ruleKey"); - response.Messages[0].Line.Should().Be(10); - response.Messages[0].Column.Should().Be(11); - response.Messages[0].EndLine.Should().Be(12); - response.Messages[0].EndColumn.Should().Be(13); - response.Messages[0].Text.Should().Be("Issue message"); - response.Messages[0].PartsMakeFlow.Should().Be(true); - response.Messages[0].Parts.Length.Should().Be(1); - } - - [TestMethod] - public void Response_WithMultipleMessages_MultipleMessageAreReturned() - { - var response = CallProtocolRead(MockResponseWithIssuesFromMultipleFiles()); - - response.Messages.Length.Should().Be(3); - response.Messages[0].Filename.Should().Be("c:\\data\\file1.cpp"); - response.Messages[1].Filename.Should().Be("e:\\data\\file2.cpp"); - response.Messages[2].Filename.Should().Be("E:\\data\\file2.cpp"); - - response.Messages[0].RuleKey.Should().Be("ruleKey1"); - response.Messages[1].RuleKey.Should().Be("ruleKey2"); - response.Messages[2].RuleKey.Should().Be("ruleKey3"); - } - - [TestMethod] - public void Read_Bad_Response_Throw() - { - Action act = () => CallProtocolRead(MockBadStartResponse()); - act.Should().ThrowExactly(); - - act = () => CallProtocolRead(MockBadEndResponse()); - act.Should().ThrowExactly(); - } - - [TestMethod] - public void Read_ResponseWithQuickFixes_QuickFixesRead() - { - var response = CallProtocolRead(MockResponseWithQuickFixes()); - - response.Messages.Length.Should().Be(1); - - response.Messages[0].Fixes.Length.Should().Be(1); - response.Messages[0].Fixes[0].Message.Should().Be("Fix message"); - response.Messages[0].Fixes[0].Edits.Length.Should().Be(1); - response.Messages[0].Fixes[0].Edits[0].Text.Should().Be("Edit message"); - response.Messages[0].Fixes[0].Edits[0].StartLine.Should().Be(1); - response.Messages[0].Fixes[0].Edits[0].StartColumn.Should().Be(2); - response.Messages[0].Fixes[0].Edits[0].EndLine.Should().Be(3); - response.Messages[0].Fixes[0].Edits[0].EndColumn.Should().Be(4); - } - - private static Response CallProtocolRead(byte[] data) - { - using (MemoryStream stream = new MemoryStream(data)) - { - BinaryReader reader = new BinaryReader(stream); - - var messages = new List(); - Protocol.Read(reader, messages.Add); - - return new Response(messages.ToArray()); - } - } - - #endregion // Protocol-level reading/writing tests - - #region Low-level reading/writing tests - - [TestMethod] - public void Write_UTF8() - { - WriteUtf("").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 0 }); - WriteUtf("a").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 1, 97 }); - WriteUtf("A").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 1, 65 }); - WriteUtf("0").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 1, 48 }); - WriteUtf("\n").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 1, 10 }); - // 3 bytes - WriteUtf("\u0800").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 3, 224, 160, 128 }); - // NUL - WriteUtf("\u0000").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 1, 0 }); - // Supplementary characters - WriteUtf("\U00010400").Should().BeEquivalentTo(new byte[] { 0, 0, 0, 4, 0xF0, 0x90, 0x90, 0x80 }); - } - - [TestMethod] - public void Read_UTF8() - { - ReadUtf(WriteUtf("")).Should().Be(""); - ReadUtf(WriteUtf("a")).Should().Be("a"); - ReadUtf(WriteUtf("A")).Should().Be("A"); - ReadUtf(WriteUtf("0")).Should().Be("0"); - ReadUtf(WriteUtf("\n")).Should().Be("\n"); - // 3 bytes - ReadUtf(WriteUtf("\u0800")).Should().Be("\u0800"); - } - - [TestMethod] - public void Write_Int() - { - WriteInt(0).Should().BeEquivalentTo(new byte[] { 0, 0, 0, 0 }); - WriteInt(1).Should().BeEquivalentTo(new byte[] { 0, 0, 0, 1 }); - WriteInt(int.MaxValue).Should().BeEquivalentTo(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }); - WriteInt(int.MinValue).Should().BeEquivalentTo(new byte[] { 0x80, 0x00, 0x00, 0x00 }); - } - - [TestMethod] - public void Read_Int() - { - ReadInt(WriteInt(0)).Should().Be(0); - ReadInt(WriteInt(int.MaxValue)).Should().Be(int.MaxValue); - ReadInt(WriteInt(int.MinValue)).Should().Be(int.MinValue); - } - - private byte[] WriteUtf(string s) - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, s); - - return stream.ToArray(); - } - } - - private string ReadUtf(byte[] bytes) - { - using (MemoryStream stream = new MemoryStream(bytes)) - { - BinaryReader reader = new BinaryReader(stream); - return Protocol.ReadUTF(reader); - } - } - - private byte[] WriteInt(int i) - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteInt(writer, i); - - return stream.ToArray(); - } - } - - private int ReadInt(byte[] bytes) - { - using (MemoryStream stream = new MemoryStream(bytes)) - { - BinaryReader reader = new BinaryReader(stream); - return Protocol.ReadInt(reader); - } - } - - #endregion // Low-level reading/writing tests - - private byte[] MockEmptyResponse() - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "OUT"); - - // 0 issues - - // 0 measures - Protocol.WriteUTF(writer, "measures"); - Protocol.WriteInt(writer, 0); - - // 0 symbols - Protocol.WriteUTF(writer, "symbols"); - Protocol.WriteInt(writer, 0); - - Protocol.WriteUTF(writer, "END"); - return stream.ToArray(); - } - } - - private byte[] MockBadStartResponse() - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "FOO"); - return stream.ToArray(); - } - } - - private byte[] MockBadEndResponse() - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "OUT"); - Protocol.WriteUTF(writer, "FOO"); - return stream.ToArray(); - } - } - - private byte[] MockResponse(string fileName = "file.cpp") - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "OUT"); - - // 1 issue - Protocol.WriteUTF(writer, "message"); - - Protocol.WriteUTF(writer, "ruleKey"); - Protocol.WriteUTF(writer, fileName); - Protocol.WriteInt(writer, 10); - Protocol.WriteInt(writer, 11); - Protocol.WriteInt(writer, 12); - Protocol.WriteInt(writer, 13); - Protocol.WriteInt(writer, 100); - Protocol.WriteUTF(writer, "Issue message"); - writer.Write(true); - - // 1 flow - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "another.cpp"); - Protocol.WriteInt(writer, 14); - Protocol.WriteInt(writer, 15); - Protocol.WriteInt(writer, 16); - Protocol.WriteInt(writer, 17); - Protocol.WriteUTF(writer, "Flow message"); - - // 0 Data Flow - Protocol.WriteInt(writer, 0); - - // 0 fixes - writer.Write(false); - Protocol.WriteInt(writer, 0); - - // 1 measure - Protocol.WriteUTF(writer, "measures"); - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, fileName); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - - byte[] execLines = new byte[] { 1, 2, 3, 4 }; - Protocol.WriteInt(writer, execLines.Length); - writer.Write(execLines); - - - // 1 symbol - Protocol.WriteUTF(writer, "symbols"); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - - Protocol.WriteUTF(writer, "END"); - return stream.ToArray(); - } - } - - private byte[] MockResponseWithIssuesFromMultipleFiles() - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "OUT"); - - // Issue 1 - Protocol.WriteUTF(writer, "message"); - Protocol.WriteUTF(writer, "ruleKey1"); - Protocol.WriteUTF(writer, "c:\\data\\file1.cpp"); - Protocol.WriteInt(writer, 10); - Protocol.WriteInt(writer, 11); - Protocol.WriteInt(writer, 12); - Protocol.WriteInt(writer, 13); - Protocol.WriteInt(writer, 100); - Protocol.WriteUTF(writer, "Issue message"); - - writer.Write(false); // no flow - Protocol.WriteInt(writer, 0); - - // 0 Data Flow - Protocol.WriteInt(writer, 0); - - // 0 fixes - writer.Write(false); - Protocol.WriteInt(writer, 0); - - // Issue 2 - Protocol.WriteUTF(writer, "message"); - Protocol.WriteUTF(writer, "ruleKey2"); - Protocol.WriteUTF(writer, "e:\\data\\file2.cpp"); - Protocol.WriteInt(writer, 10); - Protocol.WriteInt(writer, 11); - Protocol.WriteInt(writer, 12); - Protocol.WriteInt(writer, 13); - Protocol.WriteInt(writer, 100); - Protocol.WriteUTF(writer, "Issue message"); - - writer.Write(false); // no flow - Protocol.WriteInt(writer, 0); - - // 0 Data Flow - Protocol.WriteInt(writer, 0); - - // 0 fixes - writer.Write(false); - Protocol.WriteInt(writer, 0); - - // Issue 3 - Protocol.WriteUTF(writer, "message"); - Protocol.WriteUTF(writer, "ruleKey3"); - Protocol.WriteUTF(writer, "E:\\data\\file2.cpp"); - Protocol.WriteInt(writer, 10); - Protocol.WriteInt(writer, 11); - Protocol.WriteInt(writer, 12); - Protocol.WriteInt(writer, 13); - Protocol.WriteInt(writer, 100); - Protocol.WriteUTF(writer, "Issue message"); - - writer.Write(false); // no flow - Protocol.WriteInt(writer, 0); - - // 0 Data Flow - Protocol.WriteInt(writer, 0); - - // 0 fixes - writer.Write(false); - Protocol.WriteInt(writer, 0); - - // 1 measure - Protocol.WriteUTF(writer, "measures"); - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "file.cpp"); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - - byte[] execLines = new byte[] { 1, 2, 3, 4 }; - Protocol.WriteInt(writer, execLines.Length); - writer.Write(execLines); - - // 1 symbol - Protocol.WriteUTF(writer, "symbols"); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - - Protocol.WriteUTF(writer, "END"); - return stream.ToArray(); - } - } - - private byte[] MockResponseWithQuickFixes() - { - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "OUT"); - - // 1 issue - Protocol.WriteUTF(writer, "message"); - - Protocol.WriteUTF(writer, "ruleKey"); - Protocol.WriteUTF(writer, "cpp1.cpp"); - Protocol.WriteInt(writer, 10); - Protocol.WriteInt(writer, 11); - Protocol.WriteInt(writer, 12); - Protocol.WriteInt(writer, 13); - Protocol.WriteInt(writer, 100); - Protocol.WriteUTF(writer, "Issue message"); - writer.Write(true); - - // 1 flow - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "another.cpp"); - Protocol.WriteInt(writer, 14); - Protocol.WriteInt(writer, 15); - Protocol.WriteInt(writer, 16); - Protocol.WriteInt(writer, 17); - Protocol.WriteUTF(writer, "Flow message"); - - // 0 Data Flow - Protocol.WriteInt(writer, 0); - - // 1 fix - writer.Write(true); - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "Fix message"); - // 1 fix edit - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); // start line - Protocol.WriteInt(writer, 2); // end line - Protocol.WriteInt(writer, 3); // start column - Protocol.WriteInt(writer, 4); // end column - Protocol.WriteUTF(writer, "Edit message"); - - // 0 measures - Protocol.WriteUTF(writer, "measures"); - Protocol.WriteInt(writer, 0); - - // 0 symbols - Protocol.WriteUTF(writer, "symbols"); - Protocol.WriteInt(writer, 0); - - Protocol.WriteUTF(writer, "END"); - return stream.ToArray(); - } - } - - private byte[] MockResponseWithDataFlow() - { - string fileName = "file.cpp"; - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - Protocol.WriteUTF(writer, "OUT"); - - // 1 issue - Protocol.WriteUTF(writer, "message"); - - Protocol.WriteUTF(writer, "ruleKey"); - Protocol.WriteUTF(writer, fileName); - Protocol.WriteInt(writer, 10); - Protocol.WriteInt(writer, 11); - Protocol.WriteInt(writer, 12); - Protocol.WriteInt(writer, 13); - Protocol.WriteInt(writer, 100); - Protocol.WriteUTF(writer, "Issue message"); - writer.Write(true); - - // 1 flow - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "another.cpp"); - Protocol.WriteInt(writer, 14); - Protocol.WriteInt(writer, 15); - Protocol.WriteInt(writer, 16); - Protocol.WriteInt(writer, 17); - Protocol.WriteUTF(writer, "Flow message"); - - // 1 Data Flow - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "DataFlow 1"); - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, "another.cpp"); - Protocol.WriteInt(writer, 24); - Protocol.WriteInt(writer, 25); - Protocol.WriteInt(writer, 26); - Protocol.WriteInt(writer, 27); - Protocol.WriteUTF(writer, "Data Flow message"); - - // 0 fixes - writer.Write(false); - Protocol.WriteInt(writer, 0); - - // 1 measure - Protocol.WriteUTF(writer, "measures"); - Protocol.WriteInt(writer, 1); - Protocol.WriteUTF(writer, fileName); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - - byte[] execLines = new byte[] { 1, 2, 3, 4 }; - Protocol.WriteInt(writer, execLines.Length); - writer.Write(execLines); - - - // 1 symbol - Protocol.WriteUTF(writer, "symbols"); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - Protocol.WriteInt(writer, 1); - - Protocol.WriteUTF(writer, "END"); - return stream.ToArray(); - } - } - } -} diff --git a/src/CFamily/Analysis/ICFamilyIssueConverterFactory.cs b/src/CFamily/Analysis/ICFamilyIssueConverterFactory.cs deleted file mode 100644 index a67a2f98ff..0000000000 --- a/src/CFamily/Analysis/ICFamilyIssueConverterFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.CFamily.SubProcess; -using SonarLint.VisualStudio.Core.Analysis; - -namespace SonarLint.VisualStudio.CFamily.Analysis -{ - public interface ICFamilyIssueConverterFactory - { - ICFamilyIssueToAnalysisIssueConverter Create(); - } - - public interface ICFamilyIssueToAnalysisIssueConverter - { - IAnalysisIssue Convert(Message cFamilyIssue, string sqLanguage, ICFamilyRulesConfig rulesConfiguration); - } -} diff --git a/src/CFamily/Analysis/IRequest.cs b/src/CFamily/Analysis/IRequest.cs deleted file mode 100644 index 92b91784ff..0000000000 --- a/src/CFamily/Analysis/IRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Collections.Generic; -using System.IO; - -namespace SonarLint.VisualStudio.CFamily.Analysis -{ - /// - /// Encapsulates a low-level request to be sent to the CFamily subprocess - /// - /// The subprocess supports several different protocols, each of which requires a different set of inputs. - public interface IRequest - { - RequestContext Context { get; } - - /// - /// Serializes the request in the form required by the subprocess - /// - void WriteRequest(BinaryWriter writer); - - /// - /// Any environment variables that need to be passed to the subprocess. Can be null. - /// - IReadOnlyDictionary EnvironmentVariables { get; } - - /// - /// Serializes the request for diagnostic purposes - /// - void WriteRequestDiagnostics(TextWriter writer); - } -} diff --git a/src/CFamily/Analysis/IRequestFactory.cs b/src/CFamily/Analysis/IRequestFactory.cs deleted file mode 100644 index 48578181cb..0000000000 --- a/src/CFamily/Analysis/IRequestFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Threading.Tasks; - -namespace SonarLint.VisualStudio.CFamily.Analysis -{ - public interface IRequestFactory - { - /// - /// Creates for the given . - /// Returns null if request could not be created. - /// - Task TryCreateAsync(string analyzedFilePath, CFamilyAnalyzerOptions analyzerOptions); - } - - /// - /// Aggregate interface for multiple . - /// will return the first non-nullable request, - /// or null if no factory was able to create one. - /// - internal interface IRequestFactoryAggregate : IRequestFactory - {} -} diff --git a/src/CFamily/Analysis/RequestContext.cs b/src/CFamily/Analysis/RequestContext.cs deleted file mode 100644 index f2f80273a0..0000000000 --- a/src/CFamily/Analysis/RequestContext.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 SonarLint.VisualStudio.CFamily.Rules; - -namespace SonarLint.VisualStudio.CFamily.Analysis -{ - /// - /// Data class containing information about an analysis request - /// - public class RequestContext - { - public RequestContext(string language, ICFamilyRulesConfig rulesConfig, string file, string pchFile, CFamilyAnalyzerOptions analyzerOptions, bool isHeaderFile) - { - CFamilyLanguage = language; - RulesConfiguration = rulesConfig; - File = file; - PchFile = pchFile; - AnalyzerOptions = analyzerOptions; - IsHeaderFile = isHeaderFile; - } - - // Note: the language and RulesConfiguration aren't passed as part of the request to the - // CLang analyzer, but it is by SVLS used when filtering the returned issues. - public string CFamilyLanguage { get; } - - public ICFamilyRulesConfig RulesConfiguration { get; } - - /// - /// The full path to the file being analyzed - /// - public string File { get; } - - /// - /// Full path to the precompiled header file (also called the "preamble" file) - /// - /// The file may not exist. If it does, it might be out of date or for a different file. - /// However, it is the responsibility of the CFamily subprocess to handle all of those scenarios. - public string PchFile { get; } - - /// - /// Additional analysis options - /// - public CFamilyAnalyzerOptions AnalyzerOptions { get; } - - public bool IsHeaderFile { get; } - } -} diff --git a/src/CFamily/Analysis/RequestFactoryAggregate.cs b/src/CFamily/Analysis/RequestFactoryAggregate.cs deleted file mode 100644 index 3dca0643a4..0000000000 --- a/src/CFamily/Analysis/RequestFactoryAggregate.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Threading.Tasks; - -namespace SonarLint.VisualStudio.CFamily.Analysis -{ - [Export(typeof(IRequestFactoryAggregate))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal class RequestFactoryAggregate : IRequestFactoryAggregate - { - private readonly IEnumerable requestFactories; - - [ImportingConstructor] - public RequestFactoryAggregate([ImportMany] IEnumerable requestFactories) - { - this.requestFactories = requestFactories; - } - - public Task TryCreateAsync(string analyzedFilePath, CFamilyAnalyzerOptions analyzerOptions) - { - if (string.IsNullOrEmpty(analyzedFilePath)) - { - throw new ArgumentNullException(nameof(analyzedFilePath)); - } - - return TryCreate(analyzedFilePath, analyzerOptions); - } - - private async Task TryCreate(string analyzedFilePath, CFamilyAnalyzerOptions analyzerOptions) - { - foreach (var requestFactory in requestFactories) - { - var request = await requestFactory.TryCreateAsync(analyzedFilePath, analyzerOptions); - - if (request != null) - { - return request; - } - } - - return null; - } - } -} diff --git a/src/CFamily/CFamily.csproj b/src/CFamily/CFamily.csproj index 636664ad6a..19bc56c2f1 100644 --- a/src/CFamily/CFamily.csproj +++ b/src/CFamily/CFamily.csproj @@ -25,11 +25,6 @@ True Resources.resx - - True - True - Resources.resx - True True @@ -42,10 +37,6 @@ ResXFileCodeGenerator Resources.Designer.cs - - ResXFileCodeGenerator - Resources.Designer.cs - ResXFileCodeGenerator CFamilyStrings.Designer.cs diff --git a/src/CFamily/CFamilyShared.cs b/src/CFamily/CFamilyShared.cs deleted file mode 100644 index c5af58b894..0000000000 --- a/src/CFamily/CFamilyShared.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.IO; -using System.Linq; -using SonarLint.VisualStudio.Core; - -namespace SonarLint.VisualStudio.CFamily -{ - public static class CFamilyShared - { - public static readonly string CFamilyFilesDirectory = Path.Combine( - Path.GetDirectoryName(typeof(CFamilyShared).Assembly.Location), - "lib"); - - public static readonly StringComparer RuleKeyComparer = SonarRuleRepoKeys.RepoKeyComparer; - public static readonly StringComparison RuleKeyComparison = StringComparison.Ordinal; - - public static readonly string[] KnownExtensions = {".cpp", ".cxx", ".cc", ".c"}; - - private static readonly string[] KnownHeaderFileExtensions = {".h", ".hpp", ".hh", ".hxx"}; - - /// - /// Attempts to detect whether the file is C or C++ based on the file extension. - /// Returns null if the extension is not recognised. - /// - public static string FindLanguageFromExtension(string analyzedFilePath) - { - string cfamilyLanguage = null; - - // Compile files with extensions ".cpp", ".cxx" and ".cc" as Cpp and files with extension ".c" as C - if (analyzedFilePath.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) || - analyzedFilePath.EndsWith(".cxx", StringComparison.OrdinalIgnoreCase) || - analyzedFilePath.EndsWith(".cc", StringComparison.OrdinalIgnoreCase)) - { - cfamilyLanguage = SonarLanguageKeys.CPlusPlus; - } - else if (analyzedFilePath.EndsWith(".c", StringComparison.OrdinalIgnoreCase)) - { - cfamilyLanguage = SonarLanguageKeys.C; - } - - return cfamilyLanguage; - } - - /// - /// Returns true/false if the file's extension is a known header file extension. - /// - public static bool IsHeaderFileExtension(string filePath) - { - var extension = Path.GetExtension(filePath); - - return KnownHeaderFileExtensions.Any(x => - x.Equals(extension, StringComparison.OrdinalIgnoreCase)); - } - } -} diff --git a/src/CFamily/CompilationDatabase/CompilationDatabaseRequest.cs b/src/CFamily/CompilationDatabase/CompilationDatabaseRequest.cs deleted file mode 100644 index 7d9e8db996..0000000000 --- a/src/CFamily/CompilationDatabase/CompilationDatabaseRequest.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.IO; -using Newtonsoft.Json; -using SonarLint.VisualStudio.CFamily.Analysis; -using SonarLint.VisualStudio.CFamily.CMake; -using SonarLint.VisualStudio.CFamily.SubProcess; - -namespace SonarLint.VisualStudio.CFamily.CompilationDatabase -{ - /// - /// Encapsulates a low-level analysis request for the CFamily compilation database entry protocol - /// Used for both CMake and Vcx projects - /// - public class CompilationDatabaseRequest : IRequest - { - private readonly CompilationDatabaseEntry databaseEntry; - private readonly IRulesConfigProtocolFormatter rulesConfigProtocolFormatter; - - public CompilationDatabaseRequest(CompilationDatabaseEntry databaseEntry, RequestContext context, IReadOnlyDictionary environmentVariables) - : this(databaseEntry, context, environmentVariables, new RulesConfigProtocolFormatter()) - { - } - - internal CompilationDatabaseRequest(CompilationDatabaseEntry databaseEntry, - RequestContext context, IReadOnlyDictionary environmentVariables, - IRulesConfigProtocolFormatter rulesConfigProtocolFormatter) - { - this.databaseEntry = databaseEntry ?? throw new ArgumentNullException(nameof(databaseEntry)); - EnvironmentVariables = environmentVariables; - this.rulesConfigProtocolFormatter = rulesConfigProtocolFormatter; - Context = context ?? throw new ArgumentNullException(nameof(context)); - - // Must have a Command or Arguments but not both - var hasArgs = !string.IsNullOrEmpty(databaseEntry.Arguments); - var hasCmd = !string.IsNullOrEmpty(databaseEntry.Command); - - if (!hasArgs && !hasCmd || (hasArgs && hasCmd)) - { - throw new ArgumentException(CFamilyStrings.ERROR_InvalidCompilationEntry); - } - } - - public RequestContext Context { get; } - - public IReadOnlyDictionary EnvironmentVariables { get; } - - public CompilationDatabaseEntry DatabaseEntry => databaseEntry; - - public void WriteRequest(BinaryWriter writer) - { - WriteHeader(writer); - - // Required inputs - WriteSetting(writer, "File", Context.File); - WriteSetting(writer, "Directory", databaseEntry.Directory); - - if(databaseEntry.Arguments == null) - { - WriteSetting(writer, "Command", databaseEntry.Command); - } - else - { - WriteSetting(writer, "Arguments", databaseEntry.Arguments); - } - - WriteRulesConfig(writer); - - // Optional inputs - WriteSetting(writer, "PreambleFile", Context.PchFile); - WriteSetting(writer, "CreateReproducer", Context?.AnalyzerOptions?.CreateReproducer ?? false ? "true" : "false"); - WriteSetting(writer, "BuildPreamble", Context?.AnalyzerOptions?.CreatePreCompiledHeaders ?? false ? "true" : "false"); - - if (Context.IsHeaderFile) - { - WriteSetting(writer, "HeaderFileLanguage", Context.CFamilyLanguage); - } - - WriteFooter(writer); - } - - private void WriteRulesConfig(BinaryWriter writer) - { - // Optimisation - no point in calculating the active rules if we're - // creating a pre-compiled header, as they won't be used. - // However, the QualityProfile is an essential setting so we still - // have to write it. - if (!Context.AnalyzerOptions?.CreatePreCompiledHeaders ?? true) - { - var rulesProtocolFormat = rulesConfigProtocolFormatter.Format(Context.RulesConfiguration); - - WriteQualityProfile(writer, rulesProtocolFormat.QualityProfile); - WriteRuleSettings(writer, rulesProtocolFormat.RuleParameters); - } - else - { - WriteQualityProfile(writer, ""); - } - } - - private static void WriteHeader(BinaryWriter writer) - => Protocol.WriteUTF(writer, "SL-IN"); - - private static void WriteFooter(BinaryWriter writer) - => Protocol.WriteUTF(writer, "SL-END"); - - public void WriteRequestDiagnostics(TextWriter writer) - { - var data = JsonConvert.SerializeObject(databaseEntry, Formatting.Indented); - writer.Write(data); - } - - private void WriteQualityProfile(BinaryWriter writer, string qualityProfile) - { - WriteSetting(writer, "QualityProfile", qualityProfile); - } - - private void WriteRuleSettings(BinaryWriter writer, Dictionary ruleParameters) - { - foreach (var ruleParameter in ruleParameters) - { - WriteSetting(writer, ruleParameter.Key, ruleParameter.Value); - } - } - - private static void WriteSetting(BinaryWriter writer, string key, string value) - { - Protocol.WriteUTF(writer, key); - Protocol.WriteUTF(writer, value); - } - } -} diff --git a/src/CFamily/CompilationDatabase/RulesConfigProtocolFormatter.cs b/src/CFamily/CompilationDatabase/RulesConfigProtocolFormatter.cs deleted file mode 100644 index 132cfd6973..0000000000 --- a/src/CFamily/CompilationDatabase/RulesConfigProtocolFormatter.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using SonarLint.VisualStudio.CFamily.Rules; - -namespace SonarLint.VisualStudio.CFamily.CompilationDatabase -{ - public interface IRulesConfigProtocolFormatter - { - RuleConfigProtocolFormat Format(ICFamilyRulesConfig rulesConfig); - } - - public class RuleConfigProtocolFormat - { - /// - /// Comma-separated list of active rule ids - /// - public string QualityProfile { get; } - - /// - /// The key for each individual setting is in the form {ruleId}.{configname} - /// - public Dictionary RuleParameters { get; } - - public RuleConfigProtocolFormat(string qualityProfile, Dictionary ruleParameters) - { - QualityProfile = qualityProfile; - RuleParameters = ruleParameters; - } - } - - public class RulesConfigProtocolFormatter : IRulesConfigProtocolFormatter - { - public RuleConfigProtocolFormat Format(ICFamilyRulesConfig rulesConfig) - { - if (rulesConfig == null) - { - throw new ArgumentNullException(nameof(rulesConfig)); - } - - var qualityProfile = string.Join(",", rulesConfig.ActivePartialRuleKeys); - var ruleParameters = GetRuleParameters(rulesConfig); - - return new RuleConfigProtocolFormat(qualityProfile, ruleParameters); - } - - private static Dictionary GetRuleParameters(ICFamilyRulesConfig rulesConfiguration) - { - var ruleParameters = new Dictionary(); - - foreach (var ruleKey in rulesConfiguration.ActivePartialRuleKeys) - { - if (rulesConfiguration.RulesParameters.TryGetValue(ruleKey, out var ruleParams)) - { - foreach (var param in ruleParams) - { - var optionKey = ruleKey + "." + param.Key; - ruleParameters.Add(optionKey, param.Value); - } - } - } - - return ruleParameters; - } - } -} diff --git a/src/CFamily/Rules/CFamilyRulesConfigProvider.cs b/src/CFamily/Rules/CFamilyRulesConfigProvider.cs deleted file mode 100644 index 81e567dddc..0000000000 --- a/src/CFamily/Rules/CFamilyRulesConfigProvider.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.ComponentModel.Composition; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - [Export(typeof(ICFamilyRulesConfigProvider))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal class CFamilyRuleConfigProvider : ICFamilyRulesConfigProvider - { - private readonly EffectiveRulesConfigCalculator effectiveConfigCalculator; - - // Settable in constructor for testing - private readonly IRuleSettingsProviderFactory ruleSettingsProviderFactory; - private readonly ICFamilyRulesConfigProvider sonarWayProvider; - - - [ImportingConstructor] - public CFamilyRuleConfigProvider(IRuleSettingsProviderFactory ruleSettingsProviderFactory, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration, - ILogger logger) - : this(ruleSettingsProviderFactory, - new CFamilySonarWayRulesConfigProvider(CFamilyShared.CFamilyFilesDirectory), - connectedModeFeaturesConfiguration, - logger) - { - } - - public CFamilyRuleConfigProvider(IRuleSettingsProviderFactory ruleSettingsProviderFactory, - ICFamilyRulesConfigProvider sonarWayProvider, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration, - ILogger logger) - { - this.ruleSettingsProviderFactory = ruleSettingsProviderFactory; - this.sonarWayProvider = sonarWayProvider; - this.effectiveConfigCalculator = new EffectiveRulesConfigCalculator(connectedModeFeaturesConfiguration, logger); - } - - #region IRulesConfigurationProvider implementation - - public ICFamilyRulesConfig GetRulesConfiguration(string languageKey) - { - if (languageKey == null) - { - throw new ArgumentNullException(nameof(languageKey)); - } - - var language = Language.GetLanguageFromLanguageKey(languageKey); - - if (language == null) - { - throw new ArgumentNullException(nameof(language)); - } - - var ruleSettingsProvider = ruleSettingsProviderFactory.Get(language); - var settings = ruleSettingsProvider.Get(); - - var sonarWayConfig = sonarWayProvider.GetRulesConfiguration(languageKey); - return CreateConfiguration(languageKey, sonarWayConfig, settings); - } - - #endregion IRulesConfigurationProvider implementation - - protected virtual /* for testing */ ICFamilyRulesConfig CreateConfiguration(string languageKey, ICFamilyRulesConfig sonarWayConfig, RulesSettings settings) - => effectiveConfigCalculator.GetEffectiveRulesConfig(languageKey, sonarWayConfig, settings); - } -} diff --git a/src/CFamily/Rules/CFamilySonarWayRulesConfigProvider.cs b/src/CFamily/Rules/CFamilySonarWayRulesConfigProvider.cs deleted file mode 100644 index b0f604e1e7..0000000000 --- a/src/CFamily/Rules/CFamilySonarWayRulesConfigProvider.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using SonarLint.VisualStudio.Core; - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - /// - /// Loads all of the json files shipped with VSIX that contain the metadata for SonarWay - /// and presents it via the interface - /// - public sealed class CFamilySonarWayRulesConfigProvider : ICFamilyRulesConfigProvider - { - private IEnumerable AllLanguagesAllRuleKeys { get; } - private IEnumerable AllLanguagesActiveRuleKeys { get; } - private IDictionary AllLanguagesRulesMetadata { get; } - private IDictionary> AllLanguagesRulesParameters { get; } - - private IDictionary RulesByLanguage { get; } - - public CFamilySonarWayRulesConfigProvider(string rulesDirectoryPath) - { - var rulesLoader = new RulesLoader(rulesDirectoryPath); - - // Read all rules/metadata, irrespective of language. Stored in - // statics so we don't re-read the files for each language - AllLanguagesAllRuleKeys = rulesLoader.ReadRulesList(); - AllLanguagesActiveRuleKeys = rulesLoader.ReadActiveRulesList(); - AllLanguagesRulesParameters = AllLanguagesAllRuleKeys - .ToDictionary(key => key, key => rulesLoader.ReadRuleParams(key)); - AllLanguagesRulesMetadata = AllLanguagesAllRuleKeys - .ToDictionary(key => key, key => rulesLoader.ReadRuleMetadata(key)); - - RulesByLanguage = new Dictionary - { - { SonarLanguageKeys.CPlusPlus, new SingleLanguageRulesConfiguration(this, SonarLanguageKeys.CPlusPlus)}, - { SonarLanguageKeys.C, new SingleLanguageRulesConfiguration(this, SonarLanguageKeys.C)} - }; - } - - #region ICFamilyRulesConfigProvider implementation - - public ICFamilyRulesConfig GetRulesConfiguration(string languageKey) - { - RulesByLanguage.TryGetValue(languageKey, out var rulesConfiguration); - return rulesConfiguration; - } - - #endregion ICFamilyRulesConfigProvider implementation - - private sealed class SingleLanguageRulesConfiguration : ICFamilyRulesConfig - { - private static readonly StringComparer RuleKeyComparer = StringComparer.OrdinalIgnoreCase; - - public SingleLanguageRulesConfiguration(CFamilySonarWayRulesConfigProvider cache, string cFamilyLanguage) - { - LanguageKey = cFamilyLanguage; - - var ruleKeysForLanguage = cache.AllLanguagesRulesMetadata - .Where(kvp => kvp.Value.CompatibleLanguages.Contains(cFamilyLanguage, RuleKeyComparer)) - .Select(kvp => kvp.Key) - .ToArray(); - - AllPartialRuleKeys = ruleKeysForLanguage; - ActivePartialRuleKeys = cache.AllLanguagesActiveRuleKeys - .Intersect(ruleKeysForLanguage, RuleKeyComparer) - .ToArray(); - - RulesParameters = ruleKeysForLanguage - .ToDictionary(key => key, key => cache.AllLanguagesRulesParameters[key]); - RulesMetadata = ruleKeysForLanguage - .ToDictionary(key => key, key => cache.AllLanguagesRulesMetadata[key]); - } - - public string LanguageKey { get; } - - public IEnumerable AllPartialRuleKeys { get; } - - public IEnumerable ActivePartialRuleKeys { get; } - - public IDictionary> RulesParameters { get; } - - public IDictionary RulesMetadata { get; } - } - } -} diff --git a/src/CFamily/Rules/DynamicCFamilyRulesConfig.cs b/src/CFamily/Rules/DynamicCFamilyRulesConfig.cs deleted file mode 100644 index bf4733d34b..0000000000 --- a/src/CFamily/Rules/DynamicCFamilyRulesConfig.cs +++ /dev/null @@ -1,187 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - /// - /// Wrapper that handles applying customised rules settings on top of the default config - /// - /// The customised rules could be user-specified (if in standalone mode) or generated - /// from a QP (if in connected mode) - internal sealed class DynamicCFamilyRulesConfig : ICFamilyRulesConfig - { - private readonly ICFamilyRulesConfig defaultRulesConfig; - - public DynamicCFamilyRulesConfig(ICFamilyRulesConfig defaultRulesConfig, - RulesSettings customRulesSettings, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration, - ILogger logger) - : this(defaultRulesConfig, customRulesSettings, connectedModeFeaturesConfiguration, logger, new RulesConfigFixup(logger)) - { - } - - internal /* for testing */ DynamicCFamilyRulesConfig(ICFamilyRulesConfig defaultRulesConfig, - RulesSettings customRulesSettings, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration, - ILogger logger, - IRulesConfigFixup fixup) - { - this.defaultRulesConfig = defaultRulesConfig ?? throw new ArgumentNullException(nameof(defaultRulesConfig)); - - if (customRulesSettings == null) - { - throw new ArgumentNullException(nameof(customRulesSettings)); - } - - if (connectedModeFeaturesConfiguration == null) - { - throw new ArgumentNullException(nameof(connectedModeFeaturesConfiguration)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - if (customRulesSettings.Rules.Count == 0) - { - logger.WriteLine(Resources.NoCustomRulesSettings); - } - - var modifiedCustomRules = fixup.Apply(customRulesSettings, connectedModeFeaturesConfiguration); - - ActivePartialRuleKeys = CalculateActiveRules(defaultRulesConfig, modifiedCustomRules); - RulesMetadata = new Dictionary(); - RulesParameters = new Dictionary>(); - CalculateEffectiveSettings(defaultRulesConfig, modifiedCustomRules); - } - - #region IRulesConfiguration interface methods - - public string LanguageKey => defaultRulesConfig.LanguageKey; - - public IEnumerable AllPartialRuleKeys => defaultRulesConfig.AllPartialRuleKeys; - - public IEnumerable ActivePartialRuleKeys { get; } - - public IDictionary> RulesParameters { get; } - - public IDictionary RulesMetadata { get; } - - #endregion IRulesConfiguration interface methods - - private static IEnumerable CalculateActiveRules(ICFamilyRulesConfig defaultRulesConfig, RulesSettings customRulesSettings) - { - // We're only interested settings for rules that are for the same language as the supplied rules configuration. - // The rule keys in the custom rules settings include the repo prefix, but the rule keys in the default rules config do not. - var partialKeyToConfigMap = GetFilteredRulesKeyedByPartialKey(customRulesSettings, defaultRulesConfig.LanguageKey); - - var deactivatedByUser = partialKeyToConfigMap.Where(kvp => kvp.Value.Level == RuleLevel.Off).Select(kvp => kvp.Key); - var activatedByUser = partialKeyToConfigMap.Where(kvp => kvp.Value.Level == RuleLevel.On).Select(kvp => kvp.Key); - - return defaultRulesConfig.ActivePartialRuleKeys - .Concat(activatedByUser) - .Except(deactivatedByUser, CFamilyShared.RuleKeyComparer) - .Distinct(CFamilyShared.RuleKeyComparer).ToArray(); - } - - private static IDictionary GetFilteredRulesKeyedByPartialKey(RulesSettings rulesSettings, string language) - { - Debug.Assert(!string.IsNullOrEmpty(language), "language should not be null/empty"); - var languagePrefix = language + ":"; - - return rulesSettings.Rules - .Where(kvp => kvp.Key.StartsWith(languagePrefix, CFamilyShared.RuleKeyComparison)) - .ToDictionary(kvp => kvp.Key.Substring(languagePrefix.Length), kvp => kvp.Value); - } - - private void CalculateEffectiveSettings(ICFamilyRulesConfig defaultRulesConfig, RulesSettings customRulesSettings) - { - Debug.Assert(customRulesSettings?.Rules != null && customRulesSettings.Rules.Count != 0); - - foreach (var partialRuleKey in defaultRulesConfig.AllPartialRuleKeys) - { - // Not all rules have params, but all should have metadata - Debug.Assert(defaultRulesConfig.RulesMetadata[partialRuleKey] != null); - - var defaultMetadata = defaultRulesConfig.RulesMetadata[partialRuleKey]; - defaultRulesConfig.RulesParameters.TryGetValue(partialRuleKey, out var defaultParams); - - var fullRuleKey = GetFullRuleKey(defaultRulesConfig.LanguageKey, partialRuleKey); - customRulesSettings.Rules.TryGetValue(fullRuleKey, out var userRuleConfig); - - RulesMetadata[partialRuleKey] = GetEffectiveMetadata(defaultMetadata, userRuleConfig); - - var effectiveParams = GetEffectiveParameters(defaultParams, userRuleConfig?.Parameters); - if (effectiveParams != null) - { - RulesParameters[partialRuleKey] = effectiveParams; - } - } - } - - private static RuleMetadata GetEffectiveMetadata(RuleMetadata defaultMetadata, RuleConfig userConfig) - { - if (userConfig == null || !userConfig.Severity.HasValue) - { - return defaultMetadata; - } - - return new RuleMetadata - { - DefaultSeverity = userConfig.Severity.Value, - Title = defaultMetadata.Title, - CompatibleLanguages = defaultMetadata.CompatibleLanguages, - Type = defaultMetadata.Type, - Code = defaultMetadata.Code - }; - } - - internal /* for testing */ static IDictionary GetEffectiveParameters(IDictionary defaultParameters, IDictionary userParameters) - { - if (defaultParameters == null) - { - return userParameters; - } - if (userParameters == null) - { - return defaultParameters; - } - - var effectiveParams = new Dictionary(defaultParameters, StringComparer.OrdinalIgnoreCase); - foreach (var userParam in userParameters) - { - effectiveParams[userParam.Key] = userParam.Value; - } - return effectiveParams; - } - - private static string GetFullRuleKey(string language, string partialRuleKey) - => $"{language}:{partialRuleKey}"; - } -} diff --git a/src/CFamily/Rules/EffectiveRulesConfigCalculator.cs b/src/CFamily/Rules/EffectiveRulesConfigCalculator.cs deleted file mode 100644 index 79b8aed21a..0000000000 --- a/src/CFamily/Rules/EffectiveRulesConfigCalculator.cs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -/* The calculator implements simple cache to reduce the number of times the effective settings - * are recalculated (and so reduce the pressure on the garbage collector - we are creating - * multiple objects per rule and we have hundreds of rules). - * - * The cache stores one item per language - in effect it stores the last calculated config for - * that language and returns it if the source rules config and source user settings haven't - * changed. - * - * Cache hits/missed are written to the output window. - * - * Limitation: the cache is based on object identity since the source objects don't currently - * have any other mechanism that could be used. - * - * This works ok for standalone mode: the default rules config is static so the root object - * will always be same, and the IUserSettingsProvider only reloads the settings.json file - * when it changes. - * - * However, the caching doesn't work in connected mode since the connected mode settings are - * reloaded automatically every time -> object identities are different -> cache miss. - * - */ - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - /// - /// Returns the effective rules configuration to use i.e. overrides the defaults with - /// values in the user settings. - /// - /// The calculator has an internal cache to reduce unnecessary re-calculations of - /// the effective settings (and the associated object allocations). - internal class EffectiveRulesConfigCalculator - { - private readonly IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration; - private readonly ILogger logger; - private readonly RulesConfigCache configCache; - - public EffectiveRulesConfigCalculator(IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration, - ILogger logger) - { - this.connectedModeFeaturesConfiguration = connectedModeFeaturesConfiguration; - this.logger = logger ?? throw new ArgumentOutOfRangeException(nameof(logger)); - - configCache = new RulesConfigCache(); - } - - /// - /// Calculate the effective rules according to user overrides. - /// - /// - /// GetEffectiveRulesConfig could be called simultaneously from multiple threads. - /// This won't cause a crash because we are using a ConcurrentDictionary (see bug #2783). - /// It could mean that we are doing unnecessary work with multiple threads calculating the same result. - /// - public ICFamilyRulesConfig GetEffectiveRulesConfig(string languageKey, ICFamilyRulesConfig defaultRulesConfig, RulesSettings customSettings) - { - if (languageKey == null) - { - throw new ArgumentNullException(nameof(languageKey)); - } - if (defaultRulesConfig == null) - { - throw new ArgumentNullException(nameof(defaultRulesConfig)); - } - if (customSettings == null) - { - throw new ArgumentNullException(nameof(customSettings)); - } - - var effectiveConfig = configCache.FindConfig(languageKey, defaultRulesConfig, customSettings); - if (effectiveConfig != null) - { - logger.WriteLine(Resources.EffectiveRules_CacheHit); - return effectiveConfig; - } - - logger.WriteLine(Resources.EffectiveRules_CacheMiss); - - effectiveConfig = new DynamicCFamilyRulesConfig(defaultRulesConfig, customSettings, connectedModeFeaturesConfiguration, logger); - - configCache.Add(languageKey, defaultRulesConfig, customSettings, effectiveConfig); - - return effectiveConfig; - } - - /// - /// Simple cache based on object identity - /// - /// The cache holds at most one entry per language. - internal class RulesConfigCache - { - private struct CacheEntry - { - public CacheEntry(ICFamilyRulesConfig sourceConfig, RulesSettings sourceSettings, ICFamilyRulesConfig effectiveConfig) - { - SourceConfig = sourceConfig; - SourceSettings = sourceSettings; - EffectiveConfig = effectiveConfig; - } - - public ICFamilyRulesConfig SourceConfig { get; } - public RulesSettings SourceSettings { get; } - public ICFamilyRulesConfig EffectiveConfig { get; } - } - - private readonly IDictionary languageToConfigMap = new ConcurrentDictionary(); - - internal /* for testing */ int CacheCount { get { return languageToConfigMap.Count; } } - - public ICFamilyRulesConfig FindConfig(string languageKey, ICFamilyRulesConfig sourceConfig, RulesSettings sourceSettings) - { - if (!languageToConfigMap.TryGetValue(languageKey, out var cachedValue)) - { - return null; - } - - if (object.ReferenceEquals(sourceConfig, cachedValue.SourceConfig) && - object.ReferenceEquals(sourceSettings, cachedValue.SourceSettings)) - { - return cachedValue.EffectiveConfig; - } - - languageToConfigMap.Remove(languageKey); // entry doesn't match -> remove it - return null; - } - - public void Add(string languageKey, ICFamilyRulesConfig sourceConfig, RulesSettings sourceSettings, ICFamilyRulesConfig effectiveConfig) - { - languageToConfigMap[languageKey] = new CacheEntry(sourceConfig, sourceSettings, effectiveConfig); - } - } - } -} diff --git a/src/CFamily/Rules/ICFamilyRulesConfig.cs b/src/CFamily/Rules/ICFamilyRulesConfig.cs deleted file mode 100644 index eef9a5bca1..0000000000 --- a/src/CFamily/Rules/ICFamilyRulesConfig.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Collections.Generic; -using Newtonsoft.Json; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Analysis; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - public interface ICFamilyRulesConfig - { - string LanguageKey { get; } - - IEnumerable AllPartialRuleKeys { get; } - - IEnumerable ActivePartialRuleKeys { get; } - - IDictionary> RulesParameters { get; } - - IDictionary RulesMetadata { get; } - } - - public class RuleMetadata - { - [JsonProperty("title")] - public string Title { get; set; } - - [JsonProperty("type")] - public IssueType Type { get; set; } - - [JsonProperty("defaultSeverity")] - public IssueSeverity DefaultSeverity { get; set; } - - [JsonProperty("compatibleLanguages")] - public string[] CompatibleLanguages { get; set; } - - [JsonProperty("code")] - public Code Code { get; set; } - } - - public class Code - { - [JsonProperty("impacts")] - public Dictionary Impacts { get; set; } = new Dictionary(); - } - - public enum IssueType - { - CodeSmell = 0, - Bug = 1, - Vulnerability = 2, - SecurityHotspot = 3, - } -} diff --git a/src/CFamily/Rules/ICFamilyRulesConfigProvider.cs b/src/CFamily/Rules/ICFamilyRulesConfigProvider.cs deleted file mode 100644 index 2d18f99f47..0000000000 --- a/src/CFamily/Rules/ICFamilyRulesConfigProvider.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 SonarLint.VisualStudio.CFamily.Rules -{ - /// - /// Returns the CFamily rules configuration to use - /// - /// The configuration to use will depend on whether we are in standalone or connected mode, and if - /// if standalone mode on whether the user has changed the default configuration - public interface ICFamilyRulesConfigProvider - { - ICFamilyRulesConfig GetRulesConfiguration(string languageKey); - } -} diff --git a/src/CFamily/Rules/Resources.Designer.cs b/src/CFamily/Rules/Resources.Designer.cs deleted file mode 100644 index bfddd362de..0000000000 --- a/src/CFamily/Rules/Resources.Designer.cs +++ /dev/null @@ -1,126 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace SonarLint.VisualStudio.CFamily.Rules { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SonarLint.VisualStudio.CFamily.Rules.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to [CFamily] Rule settings contain entries for both legacy rule key and new rule keys. The legacy value will be ignored. Legacy key: {0}, new key: {1}. - /// - internal static string DuplicateLegacyAndNewRuleKey { - get { - return ResourceManager.GetString("DuplicateLegacyAndNewRuleKey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily] Using cached rule settings. - /// - internal static string EffectiveRules_CacheHit { - get { - return ResourceManager.GetString("EffectiveRules_CacheHit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily] Calculating effective rule settings.... - /// - internal static string EffectiveRules_CacheMiss { - get { - return ResourceManager.GetString("EffectiveRules_CacheMiss", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily] No custom rules settings - using defaults. - /// - internal static string NoCustomRulesSettings { - get { - return ResourceManager.GetString("NoCustomRulesSettings", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily] Note: the following CFamily rules are not available in SonarQube for Visual Studio: {0}. - /// - internal static string RulesUnavailableInSonarLint { - get { - return ResourceManager.GetString("RulesUnavailableInSonarLint", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily] Unable to load/locate connected mode settings. Falling back on standalone mode settings.. - /// - internal static string UnableToLoadConnectedModeSettings { - get { - return ResourceManager.GetString("UnableToLoadConnectedModeSettings", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily] Using connected mode settings. User-specified settings in settings.json will be ignored.. - /// - internal static string UsingConnectedModeSettings { - get { - return ResourceManager.GetString("UsingConnectedModeSettings", resourceCulture); - } - } - } -} diff --git a/src/CFamily/Rules/Resources.resx b/src/CFamily/Rules/Resources.resx deleted file mode 100644 index 877ff72209..0000000000 --- a/src/CFamily/Rules/Resources.resx +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - [CFamily] Rule settings contain entries for both legacy rule key and new rule keys. The legacy value will be ignored. Legacy key: {0}, new key: {1} - Output window message when settings file contains both legacy and new rule keys e.g. c:C99CommentUsage and c:S787 - - - [CFamily] Using cached rule settings - - - [CFamily] Calculating effective rule settings... - - - [CFamily] No custom rules settings - using defaults - - - [CFamily] Note: the following CFamily rules are not available in SonarQube for Visual Studio: {0} - Output window messge - - - [CFamily] Unable to load/locate connected mode settings. Falling back on standalone mode settings. - Output window message if in connected mode but couldn't find or load the connected mode settings file - - - [CFamily] Using connected mode settings. User-specified settings in settings.json will be ignored. - Output window message - - \ No newline at end of file diff --git a/src/CFamily/Rules/RulesConfigFixup.cs b/src/CFamily/Rules/RulesConfigFixup.cs deleted file mode 100644 index e25829dc6d..0000000000 --- a/src/CFamily/Rules/RulesConfigFixup.cs +++ /dev/null @@ -1,262 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Collections.Generic; -using System.Linq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - /// - /// Applies any necessary fix-ups to the rules configuration e.g. translating legacy rule keys - /// - /// - /// Legacy rule keys - /// ---------------- - /// The CFamily analyzer stopped using rule keys in the legacy "friendly name" style in v6.2: - /// all rule keys are now in the "Sxxx" format. - /// - /// There are two scenarios we need to handle: - /// 1) the user is using a version of SonarQube that has a pre-v6.2 version of the CFamily analyzer, - /// so that in Connected Mode the Quality Gate will return legacy keys; and - /// 2) the user's settings.json file contains entries using a legacy key - /// - /// Excluded rule keys - /// ------------------ - /// There are some rules we don't want to run in SonarLint: - /// * rules that need all of the files in the project to produce accurate results, and - /// * security hotspots. - /// These should never be run, even if they are explicitly enabled in custom settings. - /// - internal interface IRulesConfigFixup - { - RulesSettings Apply(RulesSettings input, IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration); - } - - internal class RulesConfigFixup : IRulesConfigFixup - { - internal static readonly string[] ExcludedRulesKeys = new string[] { - // Project-level: - "cpp:S5536", "c:S5536", - "cpp:S4830", "c:S4830", - "cpp:S5527", "c:S5527", - }; - - internal static readonly string[] HotspotRulesKeys = new[] - { - // Security hotspots: - "cpp:S5801", "c:S5801", - "cpp:S5814", "c:S5814", - "cpp:S5815", "c:S5815", - "cpp:S5816", "c:S5816", - "cpp:S5824", "c:S5824", - "cpp:S2612", "c:S2612", - "cpp:S5802", "c:S5802", - "cpp:S5849", "c:S5849", - "cpp:S5982", "c:S5982", - "cpp:S5813", "c:S5813", - "cpp:S5332", "c:S5332", - "cpp:S2068", "c:S2068", - "cpp:S2245", "c:S2245", - "cpp:S5443", "c:S5443", - "cpp:S5042", "c:S5042", - "cpp:S4790", "c:S4790", - "cpp:S1313", "c:S1313", - "cpp:S6069", "c:S6069", - }; - - private static readonly IReadOnlyDictionary partialLegacyToNewKeyMap = new Dictionary - { - // Permalink to v6.32 mapping: https://github.com/SonarSource/sonar-cpp/blob/c51c7ccb23e32f587a543a2e4b08f10e92daf2a7/sonar-cfamily-plugin/src/main/java/com/sonar/cpp/plugin/AbstractRulesDefinition.java#L35 - // This data isn't available as metadata so we have a hard-coded mapping. - { "C99CommentUsage", "S787"}, - { "SideEffectInRightHandSideOfLogical", "S912"}, - { "FunctionEllipsis", "S923"}, - { "SingleGotoOrBreakPerIteration", "S924"}, - { "ExceptionSpecificationUsage", "S2303"}, - { "PPDirectiveIndentation", "S1915"}, - { "NamespaceName", "S2304"}, - { "NonReentrantFunction", "S1912"}, - { "PPMacroName", "S1543" }, - { "ElseIfWithoutElse","S126" }, - { "SideEffectInSizeOf","S922" }, - { "NonEmptyCaseWithoutBreak","S128" }, - { "AssignmentInSubExpression", "S1121" }, - { "OctalConstantAndSequence", "S1314" }, - { "PPNonStandardInclude", "S2305" }, - { "SizeofSizeof", "S1913" }, - { "PPErrorDirectiveReached", "S1914" }, - { "UnnamedNamespaceInHeader", "S1000" }, - { "LogicalExpressionOperands", "S868" }, - { "PPIncludeCtime", "S1052" }, - { "PPIncludeCstdio", "S1055" }, - { "SingleDeclarationPerStatement", "S1659" }, - { "UsingDirective", "S1001" }, - { "EmptyThrowOutsideHandler", "S1039" }, - { "EllipsisHandlerNotLast", "S1046" }, - { "LiteralSuffix", "S818" }, - { "ExceptionInDestructor", "S1048" }, - { "IncAndDecMixedWithOtherOperators", "S881" }, - { "NarrowAndWideStringConcat", "S817" }, - { "Union", "S953" }, - { "GlobalMainFunction","S998" }, - { "GotoLabelInNestedBlock", "S1909" }, - { "PPIncludeNotAtTop","S954" }, - { "PPIncludeTime", "S991" }, - { "TrigraphUsage", "S797" }, - { "ContinueUsage", "S909" }, - { "LineLength", "S103" }, - { "FileLoc", "S104" }, - { "GotoUsage", "S907" }, - { "IdentifierLongerThan31", "S799" }, - { "GlobalNamespaceMembers", "S997" }, - { "PPIncludeNonStandardCharacters","S955" }, - { "BackJumpWithGoto", "S999" }, - { "FileComplexity", "S1908" }, - { "TabCharacter", "S105" }, - { "DigraphUsage", "S798" }, - { "InvalidEscapeSequence", "S796" }, - { "ObsoletePosixFunction", "S1911" }, - { "PPIncludeSignal", "S987" }, - { "PPBackslashNotLastCharacter", "S1916" }, - { "ClassComplexity", "S1311" }, - { "SwitchLabelPlacement", "S916" }, - { "PPIncludeStdio", "S988" }, - { "FunctionComplexity", "S1541" }, - { "CommentMixedStyles", "S1917" }, - { "OneStatementPerLine", "S122" }, - { "CommaAndOrOverloaded", "S919" }, - { "CommentedCode", "S125" }, - { "FunctionSinglePointOfExit", "S1005" }, - { "PPIncludeCHeader","S1051" }, - { "EnumPartialInitialization", "S841" }, - { "UnaryAndOverloaded", "S877" }, - { "ParsingError", "S2260" }, - { "SwitchWithoutDefault", "S131" }, - { "PPStringifyAndPastingUsage", "S968" }, - { "PPUndefUsage", "S959" }, - { "ClassName", "S101" }, - { "EmptyCompoundStatement", "S108" }, - { "PPDefineOrUndefFromBlock","S958" }, - { "PPBadIncludeForm", "S956" } - }; - - internal static readonly IReadOnlyDictionary fullLegacyToNewKeyMap = CalculateFullKeyMap(); - - private static IReadOnlyDictionary CalculateFullKeyMap() - { - var mapWithLanguagePrefixes = new Dictionary(); - foreach (var partial in partialLegacyToNewKeyMap) - { - mapWithLanguagePrefixes[$"{SonarLanguageKeys.C}:{partial.Key}"] = $"{SonarLanguageKeys.C}:{partial.Value}"; - mapWithLanguagePrefixes[$"{SonarLanguageKeys.CPlusPlus}:{partial.Key}"] = $"{SonarLanguageKeys.CPlusPlus}:{partial.Value}"; - } - return mapWithLanguagePrefixes; - } - - private readonly ILogger logger; - - public RulesConfigFixup(ILogger logger) => this.logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - - /// - /// Translates any legacy rule keys in the input to new Sxxx rule keys - /// - public RulesSettings Apply(RulesSettings input, IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration) - { - /* - - We're making a shallow copy of the list of rules. If we modify the original list, any exclusions we - add could end up be saved in the user settings.json file(if that is where the custom rules - came from). - - Modifying the saved user settings.json would be a problem in the following scenario: - * the file has a legacy key settings e.g.cpp:C99CommentUsage - * the user has multiple VS instances with "old" and "new" SonarLint instances installed - e.g.they still have an instance oF VS2015 / 2017 they need to use - - In that case, we don't want to update the legacy keys in the settings file since it would - re - enable the rules in the "old" version. - - However, _not_ updating the legacy keys in the file has a different issue: the file could - contain both old and new keys e.g. - * settings file has legacy rule key e.g.cpp:C99CommentUsage(set to "On") - * user disables the corresponding "new" rule S787. - In that case, we'll warn in the output window that the legacy setting is being ignored. - - */ - - var modifiedSettings = new RulesSettings - { - Rules = new Dictionary(input.Rules, input.Rules.Comparer) - }; - - TranslateLegacyRuleKeys(modifiedSettings); - DisableExcludedRules(modifiedSettings, connectedModeFeaturesConfiguration.IsHotspotsAnalysisEnabled()); - - return modifiedSettings; - } - - private void TranslateLegacyRuleKeys(RulesSettings settings) - { - foreach (var inputKey in settings.Rules.Keys.ToArray()) - { - if (fullLegacyToNewKeyMap.TryGetValue(inputKey, out var newKey)) - { - var inputConfig = settings.Rules[inputKey]; - settings.Rules.Remove(inputKey); - - // There might already be a setting with the new key. If so, we'll keep it and drop the legacy key setting. - if (settings.Rules.ContainsKey(newKey)) - { - logger.WriteLine(Resources.DuplicateLegacyAndNewRuleKey, inputKey, newKey); - } - else - { - logger.LogVerbose($"[CFamily] Translating legacy rule key: {inputKey} -> {newKey}"); - settings.Rules[newKey] = inputConfig; - } - } - } - } - - /// - /// Marks all excluded rules as disabled, adding them to the settings if necessary - /// - private void DisableExcludedRules(RulesSettings settings, bool hotspotsEnabled) - { - ICollection disabledRules = ExcludedRulesKeys; - - if (!hotspotsEnabled) - { - disabledRules = disabledRules.Concat(HotspotRulesKeys).ToList(); - } - - logger.WriteLine(Resources.RulesUnavailableInSonarLint, string.Join(", ", disabledRules)); - - foreach (var key in disabledRules) - { - settings.Rules[key] = new RuleConfig { Level = RuleLevel.Off }; - } - } - - } -} diff --git a/src/CFamily/Rules/RulesLoader.cs b/src/CFamily/Rules/RulesLoader.cs deleted file mode 100644 index 68d0d5fa29..0000000000 --- a/src/CFamily/Rules/RulesLoader.cs +++ /dev/null @@ -1,161 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; -using Newtonsoft.Json; - -namespace SonarLint.VisualStudio.CFamily.Rules -{ - internal class RulesLoader - { - private readonly string rulesDirectoryPath; - - public RulesLoader(string rulesDirectoryPath) - { - this.rulesDirectoryPath = rulesDirectoryPath ?? throw new ArgumentNullException(nameof(rulesDirectoryPath)); - } - - public IEnumerable ReadRulesList() - { - var rulesList = LoadCFamilyJsonFile>("RulesList.json"); - Debug.Assert(rulesList != null, "The CFamily RulesList.json should exist and not be empty"); - var misraRulesList = LoadCFamilyJsonFile>("MisraRulesList.json"); - Debug.Assert(rulesList != null, "The CFamily Misra RulesList.json should exist and not be empty"); - - rulesList.AddRange(misraRulesList); - return rulesList; - } - - public IEnumerable ReadActiveRulesList() - { - var rulesProfile = LoadCFamilyJsonFile("Sonar_way_profile.json"); - Debug.Assert(rulesProfile != null, "The CFamily Sonar_way_profile.json should exist and not be empty"); - - return rulesProfile.RuleKeys; - } - - public IDictionary ReadRuleParams(String ruleKey) - { - var ruleParams = LoadCFamilyJsonFile(ruleKey + "_params.json"); - - if (ruleParams == null) - { - return new Dictionary(); - } - - var result = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var param in ruleParams) - { - result.Add(param.Key, param.DefaultValue); - } - return result; - } - - public RuleMetadata ReadRuleMetadata(String ruleKey) - { - var ruleMetadata = LoadCFamilyJsonFile(ruleKey + ".json"); - - if (ruleMetadata == null) - { - throw new FileNotFoundException("Unable to find metadata of rule: " + ruleKey); - } - - return ruleMetadata; - } - - private T LoadCFamilyJsonFile(string fileName) where T : class - { - string path = Path.Combine(this.rulesDirectoryPath, fileName); - if (!File.Exists(path)) - { - return default(T); - } - - var data = JsonConvert.DeserializeObject(File.ReadAllText(path, Encoding.UTF8), new SonarTypeConverter()); - return data; - } - - private class RulesProfile - { - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("ruleKeys")] - public List RuleKeys { get; set; } - } - - private class RuleParameter - { - [JsonProperty("key")] - public string Key { get; set; } - - [JsonProperty("description")] - public string Description { get; set; } - - [JsonProperty("defaultValue")] - public string DefaultValue { get; set; } - - [JsonProperty("type")] - public string Type { get; set; } - } - - /// - /// Custom converter to the protobuf issue Type enum - /// - internal class SonarTypeConverter : Newtonsoft.Json.JsonConverter - { - public override bool CanConvert(Type objectType) => - objectType == typeof(IssueType); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var serializedString = (string)reader.Value; - - // The names of the CodeSmell enum doesn't map directly to the serialized string so - // we can't use the default JSON StringEnumSerializer - if (serializedString.Equals("CODE_SMELL", StringComparison.OrdinalIgnoreCase)) - { - return IssueType.CodeSmell; - } - - if (serializedString.Equals("SECURITY_HOTSPOT", StringComparison.OrdinalIgnoreCase)) - { - return IssueType.SecurityHotspot; - } - - if (Enum.TryParse(serializedString, true /* ignore case */, out IssueType data)) - { - return data; - } - - throw new JsonSerializationException($"Unrecognized IssueType value: {serializedString}"); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - } - } -} diff --git a/src/CFamily/Subprocess/CFamilyStrings.Designer.cs b/src/CFamily/Subprocess/CFamilyStrings.Designer.cs deleted file mode 100644 index 2c9a21d6df..0000000000 --- a/src/CFamily/Subprocess/CFamilyStrings.Designer.cs +++ /dev/null @@ -1,256 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace SonarLint.VisualStudio.CFamily.SubProcess { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class CFamilyStrings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal CFamilyStrings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SonarLint.VisualStudio.CFamily.SubProcess.CFamilyStrings", typeof(CFamilyStrings).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Invalid compilation database entry. Either Command or Arguments must be supplied, but not both.. - /// - internal static string ERROR_InvalidCompilationEntry { - get { - return ResourceManager.GetString("ERROR_InvalidCompilationEntry", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Execution failed. The specified executable does not exist: {0}. - /// - internal static string ERROR_ProcessRunner_ExeNotFound { - get { - return ResourceManager.GetString("ERROR_ProcessRunner_ExeNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <sensitive data removed>. - /// - internal static string MSG_CmdLine_SensitiveCmdLineArgsAlternativeText { - get { - return ResourceManager.GetString("MSG_CmdLine_SensitiveCmdLineArgsAlternativeText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Executing file {0} - /// Args: {1} - /// Working directory: {2} - /// Process id: {3}. - /// - internal static string MSG_ExecutingFile { - get { - return ResourceManager.GetString("MSG_ExecutingFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Execution cancelled.. - /// - internal static string MSG_ExecutionCancelled { - get { - return ResourceManager.GetString("MSG_ExecutionCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Process returned exit code {0}. - /// - internal static string MSG_ExecutionExitCode { - get { - return ResourceManager.GetString("MSG_ExecutionExitCode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to See above for more information.. - /// - internal static string MSG_GenericAnalysisFailed { - get { - return ResourceManager.GetString("MSG_GenericAnalysisFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Refreshing PCH file for {0}. PCH file location: {1}. - /// - internal static string MSG_PchSaved { - get { - return ResourceManager.GetString("MSG_PchSaved", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DEBUG: . - /// - internal static string MSG_Prefix_DEBUG { - get { - return ResourceManager.GetString("MSG_Prefix_DEBUG", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ERROR: . - /// - internal static string MSG_Prefix_ERROR { - get { - return ResourceManager.GetString("MSG_Prefix_ERROR", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to WARNING: . - /// - internal static string MSG_Prefix_WARN { - get { - return ResourceManager.GetString("MSG_Prefix_WARN", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Process {0} was killed.. - /// - internal static string MSG_ProessRunner_ProcessKilled { - get { - return ResourceManager.GetString("MSG_ProessRunner_ProcessKilled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reproducer file saved at: {0} . - /// - internal static string MSG_ReproducerSaved { - get { - return ResourceManager.GetString("MSG_ReproducerSaved", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Request config saved at: {0} . - /// - internal static string MSG_RequestConfigSaved { - get { - return ResourceManager.GetString("MSG_RequestConfigSaved", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Overwriting the value of environment variable '{0}'. Old value: {1}, new value: {2}. - /// - internal static string MSG_Runner_OverwritingEnvVar { - get { - return ResourceManager.GetString("MSG_Runner_OverwritingEnvVar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Setting environment variable '{0}'. Value: {1}. - /// - internal static string MSG_Runner_SettingEnvVar { - get { - return ResourceManager.GetString("MSG_Runner_SettingEnvVar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to retrieve the configuration for file '{0}'. - /// Check the file is part of a supported project type in the current solution.. - /// - internal static string MSG_UnableToCreateConfig { - get { - return ResourceManager.GetString("MSG_UnableToCreateConfig", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unable to locate the CFamily analyzer exe. - /// - internal static string MSG_UnableToLocateSubProcessExe { - get { - return ResourceManager.GetString("MSG_UnableToLocateSubProcessExe", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily Analysis] Internal error calling the analysis subprocess: {0}. - /// - internal static string MsgHandler_ReportInvalidInput { - get { - return ResourceManager.GetString("MsgHandler_ReportInvalidInput", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily Analysis] Internal error in the analysis subprocess: {0}. - /// - internal static string MsgHandler_ReportUnexpectedFailure { - get { - return ResourceManager.GetString("MsgHandler_ReportUnexpectedFailure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [CFamily Analysis] Unsupported configuration: {0}. - /// - internal static string MsgHandler_ReportUnsupportedConfiguration { - get { - return ResourceManager.GetString("MsgHandler_ReportUnsupportedConfiguration", resourceCulture); - } - } - } -} diff --git a/src/CFamily/Subprocess/CFamilyStrings.resx b/src/CFamily/Subprocess/CFamilyStrings.resx deleted file mode 100644 index d5c01492b6..0000000000 --- a/src/CFamily/Subprocess/CFamilyStrings.resx +++ /dev/null @@ -1,189 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Invalid compilation database entry. Either Command or Arguments must be supplied, but not both. - - - Execution failed. The specified executable does not exist: {0} - - - [CFamily Analysis] Internal error calling the analysis subprocess: {0} - - - [CFamily Analysis] Internal error in the analysis subprocess: {0} - - - [CFamily Analysis] Unsupported configuration: {0} - - - <sensitive data removed> - - - Executing file {0} - Args: {1} - Working directory: {2} - Process id: {3} - - - Execution cancelled. - - - Process returned exit code {0} - - - See above for more information. - Generic failure message logged by the CLangAnalyzer. Assumes more detailed information will already have been logged e.g. by the MessageHandler. -This message is only displayed in the Output Window (not the status bar). - - - Refreshing PCH file for {0}. PCH file location: {1} - - - DEBUG: - - - ERROR: - - - WARNING: - - - Process {0} was killed. - - - Reproducer file saved at: {0} - - - Request config saved at: {0} - - - Overwriting the value of environment variable '{0}'. Old value: {1}, new value: {2} - - - Setting environment variable '{0}'. Value: {1} - - - Unable to retrieve the configuration for file '{0}'. - Check the file is part of a supported project type in the current solution. - - - Unable to locate the CFamily analyzer exe - - \ No newline at end of file diff --git a/src/CFamily/Subprocess/IProcessRunner.cs b/src/CFamily/Subprocess/IProcessRunner.cs deleted file mode 100644 index 5ee2694867..0000000000 --- a/src/CFamily/Subprocess/IProcessRunner.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 SonarLint.VisualStudio.CFamily.SubProcess -{ - internal interface IProcessRunner - { - void Execute(ProcessRunnerArguments runnerArgs); - } -} diff --git a/src/CFamily/Subprocess/MessageHandler.cs b/src/CFamily/Subprocess/MessageHandler.cs deleted file mode 100644 index c6b18b8ce9..0000000000 --- a/src/CFamily/Subprocess/MessageHandler.cs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Linq; -using SonarLint.VisualStudio.CFamily.Analysis; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Analysis; -using SonarLint.VisualStudio.Core.ETW; -using SonarLint.VisualStudio.Core.Helpers; - -namespace SonarLint.VisualStudio.CFamily.SubProcess -{ - /// - /// Handles messages returned by the CFamily subprocess.exe - /// - internal interface IMessageHandler - { - /// - /// The number of analysis issues processed by the message handler - /// - /// Messages with internal rule keys and message for files other than the - /// file being analyzed are ignored - int IssueCount { get; } - - /// - /// True if the analysis completed successfully, otherwise false - /// - /// The analysis will be treated as having failed if any "error" internal messages are received - bool AnalysisSucceeded { get; } - - void HandleMessage(Message message); - } - - /// - /// No-op implementation - used when there is not a valid issue consumer - /// - internal class NoOpMessageHandler : IMessageHandler - { - /// - /// Singleton no-op message handler - /// - public static readonly IMessageHandler Instance = new NoOpMessageHandler(); - - public int IssueCount { get; } = 0; - - public bool AnalysisSucceeded => true; - - public void HandleMessage(Message message) { /* no-op */ } - } - - internal class MessageHandler : IMessageHandler - { - private readonly IRequest request; - private readonly IIssueConsumer issueConsumer; - private readonly ICFamilyIssueToAnalysisIssueConverter issueConverter; - private readonly ILogger logger; - - public int IssueCount { get; private set; } - - public bool AnalysisSucceeded { get; private set; } = true; - - public MessageHandler(IRequest request, IIssueConsumer issueConsumer, ICFamilyIssueToAnalysisIssueConverter issueConverter, ILogger logger) - { - this.request = request; - this.issueConsumer = issueConsumer; - this.issueConverter = issueConverter; - this.logger = logger; - } - - public void HandleMessage(Message message) - { - CodeMarkers.Instance.CFamilyHandleMessageStart(request.Context.File); - - // Handle known internal rule keys - used to return info/warnings - switch (message.RuleKey) - { - case "internal.UnsupportedConfig": // the user has specified an unsupported configuration option - log it - AnalysisSucceeded = false; - logger.WriteLine(CFamilyStrings.MsgHandler_ReportUnsupportedConfiguration, message.Text); - break; - - case "internal.InvalidInput": // subprocess has been called incorrectly by SonarLint - AnalysisSucceeded = false; - logger.WriteLine(CFamilyStrings.MsgHandler_ReportInvalidInput, message.Text); - break; - - case "internal.UnexpectedFailure": // unexpected failure in the subprocess - AnalysisSucceeded = false; - logger.WriteLine(CFamilyStrings.MsgHandler_ReportUnexpectedFailure, message.Text); - break; - - // Rules that start with internal shouldn't be treated as issues. - // Some of them should be handled like `internal.fileDependency`. See: https://github.com/SonarSource/sonarlint-visualstudio/issues/2611 - // Others should can simply ignored like `internal.z3RefutationRate`, which is used to log in the scanner how many issues are rejected by the Z3 solver - case string s when s.StartsWith("internal."): - break; - - default: // assume anything else is an analysis issue - HandleAnalysisIssue(message); - break; - } - - CodeMarkers.Instance.CFamilyHandleMessageStop(); - } - - private void HandleAnalysisIssue(Message message) - { - if (string.IsNullOrEmpty(message.Filename) // info/error messages might not have a file name - || !PathHelper.IsMatchingPath(message.Filename, request.Context.File)) // Ignore issues for other files (e.g. issues reported against header when analysing a source file) - { - return; - } - - if (!IsIssueForActiveRule(message, request.Context.RulesConfiguration)) - { - return; - } - - IssueCount++; - var issue = issueConverter.Convert(message, request.Context.CFamilyLanguage, request.Context.RulesConfiguration); - - // Note: the file being analyzed might have been closed by the time the analysis results are - // returned. This doesn't cause a crash; all active taggers will have been detached from the - // TextBufferIssueTracker when the file was closed, but the TextBufferIssueTracker will - // still exist and handle the call. - // todo https://sonarsource.atlassian.net/browse/SLVS-1661 - issueConsumer.SetIssues(request.Context.File, new[] { issue }); - } - - internal /* for testing */ static bool IsIssueForActiveRule(Message message, ICFamilyRulesConfig rulesConfiguration) - { - // Currently (v6.3) the subprocess.exe will always run the native CLang rules, so those issues - // could be returned even if they were not activated in the profile. - - // In addition, in v6.4+ there are internal rules that are always enabled and will always return - // issues. Filtering for active rules will also remove those internal issues since the corresponding - // rules will never be active in a quality profile. - return rulesConfiguration.ActivePartialRuleKeys.Contains(message.RuleKey, CFamilyShared.RuleKeyComparer); - } - } -} diff --git a/src/CFamily/Subprocess/PortedFromJava/Analyzer.cs b/src/CFamily/Subprocess/PortedFromJava/Analyzer.cs deleted file mode 100644 index c2a3a0953d..0000000000 --- a/src/CFamily/Subprocess/PortedFromJava/Analyzer.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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. - */ - -/** - * This is a port of the protocol implemented between Java and C++ - * https://github.com/SonarSource/sonar-cpp/blob/master/sonar-cfamily-plugin/src/main/java/com/sonar/cpp/analyzer/Analyzer.java - */ -namespace SonarLint.VisualStudio.CFamily.SubProcess -{ - internal class Response - { - public Message[] Messages { get; } - - public Response(Message[] messages) - { - Messages = messages; - } - } - - public class DataFlow - { - public string Description { get; } - public MessagePart[] Steps { get; } - - public DataFlow(string description, MessagePart[] steps) - { - this.Description = description; - this.Steps = steps; - } - } - - public class MessagePart - { - public string Filename; - public int Line { get; } - public int Column { get; } - public int EndLine { get; } - public int EndColumn { get; } - public string Text { get; } - - public MessagePart(string filename, int line, int column, int endLine, int endColumn, string text) - { - Filename = filename; - Line = line; - Column = column; - EndLine = endLine; - EndColumn = endColumn; - Text = text; - } - } - - public class Message : MessagePart - { - public string RuleKey { get; } - public bool PartsMakeFlow { get; } - public MessagePart[] Parts { get; } - public Fix[] Fixes { get; } - - // SLVS: we expect the class to be JSON-serializable for the ease of testing, and therefore the ctor parameter names must match the property names - // for the default Newtonsoft serializer to work correctly. - public Message(string ruleKey, string filename, int line, int column, int endLine, int endColumn, string text, bool partsMakeFlow, MessagePart[] parts, Fix[] fixes) - : base(filename, line, column, endLine, endColumn, text) - { - RuleKey = ruleKey; - PartsMakeFlow = partsMakeFlow; - Parts = parts; - Fixes = fixes; - } - } - - public class Fix - { - public string Message { get; } - public Edit[] Edits { get; } - - public Fix(string message, Edit[] edits) - { - Message = message; - Edits = edits; - } - } - - public class Edit - { - public int StartLine { get; } - public int StartColumn { get; } - public int EndLine { get; } - public int EndColumn { get; } - public string Text { get; } - - public Edit(int startLine, int startColumn, int endLine, int endColumn, string text) - { - StartLine = startLine; - StartColumn = startColumn; - EndLine = endLine; - EndColumn = endColumn; - Text = text; - } - } -} diff --git a/src/CFamily/Subprocess/PortedFromJava/Protocol.cs b/src/CFamily/Subprocess/PortedFromJava/Protocol.cs deleted file mode 100644 index adab282de7..0000000000 --- a/src/CFamily/Subprocess/PortedFromJava/Protocol.cs +++ /dev/null @@ -1,238 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.IO; -using System.Text; - -/** - * This is a port of the protocol implemented between Java and C++ - * https://github.com/SonarSource/sonar-cpp/blob/master/sonar-cfamily-plugin/src/main/java/com/sonar/cpp/analyzer/Protocol.java - * - * Note: the ported code also needs to deal with the fact that the Java DataOutputStream/DataInputStreams are big-endian whereas - * the C# BinaryWriter/Reader are little-endian. - */ -namespace SonarLint.VisualStudio.CFamily.SubProcess -{ - internal static class Protocol - { - internal /* for testing */ static void WriteInt(BinaryWriter writer, int i) - { - // Big endian conversion - byte[] temp = BitConverter.GetBytes(i); - if (BitConverter.IsLittleEndian) - { - Array.Reverse(temp); - } - writer.Write(temp); - } - - internal /* for testing */ static void WriteUTF(BinaryWriter writer, string str) - { - byte[] bytes = Encoding.UTF8.GetBytes(str); - WriteInt(writer, bytes.Length); - writer.Write(bytes); - } - - internal /* for testing */ static string ReadUTF(BinaryReader reader) - { - int size = ReadInt(reader); - return Encoding.UTF8.GetString(reader.ReadBytes(size)); - } - - internal /* for testing */ static int ReadInt(BinaryReader reader) - { - // Big endian conversion - byte[] temp = reader.ReadBytes(4); - if (BitConverter.IsLittleEndian) - { - Array.Reverse(temp); - } - return BitConverter.ToInt32(temp, 0); - } - - internal /* for testing */ static ushort ReadUShort(BinaryReader reader) - { - // Big endian conversion - byte[] temp = reader.ReadBytes(2); - if (BitConverter.IsLittleEndian) - { - Array.Reverse(temp); - } - return BitConverter.ToUInt16(temp, 0); - } - - /** - * This method does not close the provided stream. - */ - public static void Read(BinaryReader reader, Action handleIssue) - { - if ("OUT" != ReadUTF(reader)) - { - throw new InvalidDataException("Communication issue with the C/C++ analyzer: OUT expected"); - } - - - while (true) { - switch (ReadUTF(reader)) { - default: - throw new InvalidDataException("Communication issue with the C/C++ analyzer"); - case "message": - var message = readMessage(reader); - handleIssue(message); - break; - case "measures": - // Skip measures - readMeasures(reader); - break; - case "symbols": - // Skip symbols - readSymbols(reader); - break; - case "END": - return; - } - } - } - - private static Message readMessage(BinaryReader reader) { - string ruleKey = ReadUTF(reader); - string filename = ReadUTF(reader); - int line = ReadInt(reader); - int column = ReadInt(reader); - int endLine = ReadInt(reader); - int endColumn = ReadInt(reader); - // Skip remediation cost - ReadInt(reader); - string text = ReadUTF(reader); - bool partsMakeFlow = reader.ReadBoolean(); - MessagePart[] parts = ReadMessageParts(reader); - _ = ReadDataFlows(reader); - reader.ReadBoolean(); - Fix[] fixes = ReadFixes(reader); - return new Message(ruleKey, filename, line, column, endLine, endColumn, text, partsMakeFlow, parts, fixes); - } - - private static Fix[] ReadFixes(BinaryReader reader) { - int fixesCount = ReadInt(reader); - if (fixesCount == 0) - { - return Array.Empty(); - } - Fix[] fixes = new Fix[fixesCount]; - for (int i = 0; i < fixes.Length; i++) - { - fixes[i] = new Fix( - /* message= */ ReadUTF(reader), - ReadEdits(reader)); - } - return fixes; - } - - private static Edit[] ReadEdits(BinaryReader reader) { - int editsCount = ReadInt(reader); - Edit[] edits = new Edit[editsCount]; - for (int i = 0; i < edits.Length; i++) - { - edits[i] = new Edit( - /* startLine= */ ReadInt(reader), - /* startColumn= */ ReadInt(reader), - /* endLine= */ ReadInt(reader), - /* endColumn= */ ReadInt(reader), - /* text= */ ReadUTF(reader)); - } - return edits; - } - - private static MessagePart[] ReadMessageParts(BinaryReader reader) - { - int partsCount = ReadInt(reader); - if (partsCount == 0) - { - return Array.Empty(); - } - MessagePart[] parts = new MessagePart[partsCount]; - for (int j = 0; j < parts.Length; j++) - { - parts[j] = new MessagePart( - /* filename= */ ReadUTF(reader), - /* line= */ ReadInt(reader), - /* column= */ ReadInt(reader), - /* endLine= */ ReadInt(reader), - /* endColumn= */ ReadInt(reader), - /* text= */ ReadUTF(reader)); - } - return parts; - } - - private static void readMeasures(BinaryReader reader) { - int nbMeasures = ReadInt(reader); - for (int i = 0; i < nbMeasures; i++) - { - /* filename */ - ReadUTF(reader); - /* classes */ - ReadInt(reader); - /* functions */ - ReadInt(reader); - /* statements */ - ReadInt(reader); - /* complexity */ - ReadInt(reader); - /* cognitiveComplexity */ - ReadInt(reader); - /* exec lines */ - reader.ReadBytes(ReadInt(reader)); - } - } - - private static void readSymbols(BinaryReader reader) { - int nbSymbols = ReadInt(reader); - for (int i = 0; i < nbSymbols; i++) - { - int nbSymbolRefs = ReadInt(reader); - for (int j = 0; j < nbSymbolRefs; j++) - { - /* line */ - ReadInt(reader); - /* column */ - ReadInt(reader); - /* endLine */ - ReadInt(reader); - /* endColumn */ - ReadInt(reader); - } - } - } - - private static DataFlow[] ReadDataFlows(BinaryReader reader) - { - int flowCount = ReadInt(reader); - if (flowCount == 0) { return null; } - - var dataFlows = new DataFlow[flowCount]; - for (int i = 0; i - /// Helper class to run an executable and capture the output - /// - internal sealed class ProcessRunner : IProcessRunner - { - public const int ErrorCode = 1; - - private readonly ILogger logger; - private readonly ISonarLintSettings settings; - - public ProcessRunner(ISonarLintSettings settings, ILogger logger) - { - this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); - this.settings = settings ?? throw new ArgumentNullException(nameof(settings)); - } - - public int ExitCode { get; private set; } - - /// - /// Runs the specified executable and communicates with it via Standard IO streams. - /// The method blocks until the handler has read to the end of the output stream, or when the cancellation token is cancelled. - /// - /// - /// Child processes do not inherit the env variables from the parent automatically. - /// The stream reader callbacks are executed on the original calling thread. - /// Errors and timeouts are written to the logger, which in turn writes to the output window. The caller won't see them and has no way of checking the outcome. - /// - public void Execute(ProcessRunnerArguments runnerArgs) - { - if (runnerArgs == null) - { - throw new ArgumentNullException(nameof(runnerArgs)); - } - - Debug.Assert(!string.IsNullOrWhiteSpace(runnerArgs.ExeName), - "Process runner exe name should not be null/empty"); - - if (!File.Exists(runnerArgs.ExeName)) - { - LogError(CFamilyStrings.ERROR_ProcessRunner_ExeNotFound, runnerArgs.ExeName); - ExitCode = ErrorCode; - return; - } - - var psi = new ProcessStartInfo - { - FileName = runnerArgs.ExeName, - RedirectStandardError = true, - RedirectStandardOutput = true, - RedirectStandardInput = true, - UseShellExecute = false, // required if we want to capture the error output - ErrorDialog = false, - CreateNoWindow = true, - Arguments = runnerArgs.GetEscapedArguments(), - WorkingDirectory = runnerArgs.WorkingDirectory - }; - - SetEnvironmentVariables(psi, runnerArgs.EnvironmentVariables); - - var hasProcessStarted = false; - var isRunningProcessCancelled = false; - - using (var process = new Process()) - using (runnerArgs.CancellationToken.Register(() => - { - LogMessage(CFamilyStrings.MSG_ExecutionCancelled); - - lock (process) - { - if (!hasProcessStarted) - { - // Cancellation was requested before process started - do nothing - return; - } - } - // Cancellation was requested after process started - kill it - isRunningProcessCancelled = true; - KillProcess(process); - })) - { - process.ErrorDataReceived += OnErrorDataReceived; - process.StartInfo = psi; - - lock (process) - { - if (!runnerArgs.CancellationToken.IsCancellationRequested) - { - process.Start(); - hasProcessStarted = true; - } - else - { - LogMessage(CFamilyStrings.MSG_ExecutionCancelled); - return; - } - } - - process.BeginErrorReadLine(); - - // Warning: do not log the raw command line args as they - // may contain sensitive data - LogDebug(CFamilyStrings.MSG_ExecutingFile, - runnerArgs.ExeName, - runnerArgs.AsLogText(), - runnerArgs.WorkingDirectory, - process.Id); - - try - { - runnerArgs.HandleInputStream?.Invoke(process.StandardInput); - - // the caller needs to start a blocking read operation, otherwise the method would exit. - runnerArgs.HandleOutputStream?.Invoke(process.StandardOutput); - - // Give any asynchronous events the chance to complete - process.WaitForExit(); - ExitCode = process.ExitCode; - LogDebug(CFamilyStrings.MSG_ExecutionExitCode, process.ExitCode); - } - catch (Exception ex) when (isRunningProcessCancelled && !ErrorHandler.IsCriticalException(ex)) - { - // If a process is cancelled mid-stream, an exception will be thrown. - } - } - } - private void KillProcess(Process process) - { - try - { - if (process != null && !process.HasExited) - { - LogDebug(CFamilyStrings.MSG_ProessRunner_ProcessKilled, process.Id); - process.Kill(); - } - } - catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) - { - // It's possible that the process exited just between the IF and the Kill(), in which case an exception is thrown. - } - } - - private void SetEnvironmentVariables(ProcessStartInfo psi, IReadOnlyDictionary envVariables) - { - if (envVariables == null) - { - return; - } - - foreach (var envVariable in envVariables) - { - Debug.Assert(!string.IsNullOrEmpty(envVariable.Key), "Env variable name cannot be null or empty"); - - if (psi.EnvironmentVariables.ContainsKey(envVariable.Key)) - { - LogDebug(CFamilyStrings.MSG_Runner_OverwritingEnvVar, envVariable.Key, psi.EnvironmentVariables[envVariable.Key], envVariable.Value); - } - else - { - LogDebug(CFamilyStrings.MSG_Runner_SettingEnvVar, envVariable.Key, envVariable.Value); - } - psi.EnvironmentVariables[envVariable.Key] = envVariable.Value; - } - } - - private void OnErrorDataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data != null) - { - LogError(e.Data); - } - } - - private void LogMessage(string message, params object[] args) - { - var formattedMessage = GetFormattedMessage(message, args); - logger.WriteLine(formattedMessage); - } - - private void LogError(string message, params object[] args) - { - LogMessage(CFamilyStrings.MSG_Prefix_ERROR + message, args); - } - - private void LogDebug(string message, params object[] args) - { - if (settings.DaemonLogLevel == DaemonLogLevel.Verbose) - { - LogMessage(CFamilyStrings.MSG_Prefix_DEBUG + message, args); - } - } - - private static string GetFormattedMessage(string message, params object[] args) - { - var finalMessage = message; - if (args != null && args.Length > 0) - { - finalMessage = string.Format(CultureInfo.CurrentCulture, finalMessage ?? string.Empty, args); - } - - return finalMessage; - } - } -} diff --git a/src/CFamily/Subprocess/ProcessRunnerArguments.cs b/src/CFamily/Subprocess/ProcessRunnerArguments.cs deleted file mode 100644 index 4cf267d8ea..0000000000 --- a/src/CFamily/Subprocess/ProcessRunnerArguments.cs +++ /dev/null @@ -1,244 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; - -namespace SonarLint.VisualStudio.CFamily.SubProcess -{ - /// - /// Data class containing parameters required to execute a new process - /// - internal class ProcessRunnerArguments - { - // NOTE: the list of sensitive keys is hard-coded in the S4MSB. - // We've made it a property here to limit the amount of refactoring required to use the - // class here, although currently SLVS doesn't need to pass any sensitive arguments. - - /// - /// Strings that are used to indicate arguments that contain - /// sensitive data that should not be logged - /// - public IEnumerable SensitivePropertyKeys { get; set; } = Enumerable.Empty(); - - public ProcessRunnerArguments(string exeName, bool isBatchScript) - { - if (string.IsNullOrWhiteSpace(exeName)) - { - throw new ArgumentNullException(nameof(exeName)); - } - - ExeName = exeName; - IsBatchScript = isBatchScript; - } - - #region Public properties - - public string ExeName { get; } - - public CancellationToken CancellationToken { get; set; } - - /// - /// Non-sensitive command line arguments (i.e. ones that can safely be logged). Optional. - /// - public IEnumerable CmdLineArgs { get; set; } - - public string WorkingDirectory { get; set; } - - private bool IsBatchScript { get; set; } - - /// - /// Additional environments variables that should be set/overridden for the process. Can be null. - /// - public IReadOnlyDictionary EnvironmentVariables { get; set; } - - public Action HandleInputStream { get; set; } - public Action HandleOutputStream { get; set; } - - public string GetEscapedArguments() - { - if (CmdLineArgs == null) - { return null; } - - var result = string.Join(" ", CmdLineArgs.Select(a => EscapeArgument(a))); - - if (IsBatchScript) - { - result = ShellEscape(result); - } - - return result; - } - - /// - /// Returns the string that should be used when logging command line arguments - /// (sensitive data will have been removed) - /// - public string AsLogText() - { - if (CmdLineArgs == null) - { return null; } - - var hasSensitiveData = false; - - var sb = new StringBuilder(); - - foreach (var arg in CmdLineArgs) - { - if (ContainsSensitiveData(arg)) - { - hasSensitiveData = true; - } - else - { - sb.Append(arg); - sb.Append(" "); - } - } - - if (hasSensitiveData) - { - sb.Append(CFamilyStrings.MSG_CmdLine_SensitiveCmdLineArgsAlternativeText); - } - - return sb.ToString(); - } - - /// - /// Determines whether the text contains sensitive data that - /// should not be logged/written to file - /// - private bool ContainsSensitiveData(string text) - { - Debug.Assert(SensitivePropertyKeys != null, "SensitiveDataMarkers array should not be null"); - - if (text == null) - { - return false; - } - - return SensitivePropertyKeys.Any(marker => text.IndexOf(marker, StringComparison.OrdinalIgnoreCase) > -1); - } - - /// - /// The CreateProcess Win32 API call only takes 1 string for all arguments. - /// Ultimately, it is the responsibility of each program to decide how to split this string into multiple arguments. - /// - /// See: - /// https://blogs.msdn.microsoft.com/oldnewthing/20100917-00/?p=12833/ - /// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ - /// http://www.daviddeley.com/autohotkey/parameters/parameters.htm - /// - private static string EscapeArgument(string arg) - { - Debug.Assert(arg != null, "Not expecting an argument to be null"); - - var sb = new StringBuilder(); - - sb.Append("\""); - for (var i = 0; i < arg.Length; i++) - { - var numberOfBackslashes = 0; - for (; i < arg.Length && arg[i] == '\\'; i++) - { - numberOfBackslashes++; - } - - if (i == arg.Length) - { - // - // Escape all backslashes, but let the terminating - // double quotation mark we add below be interpreted - // as a meta-character. - // - sb.Append('\\', numberOfBackslashes * 2); - } - else if (arg[i] == '"') - { - // - // Escape all backslashes and the following - // double quotation mark. - // - sb.Append('\\', numberOfBackslashes * 2 + 1); - sb.Append(arg[i]); - } - else - { - // - // Backslashes aren't special here. - // - sb.Append('\\', numberOfBackslashes); - sb.Append(arg[i]); - } - } - sb.Append("\""); - - return sb.ToString(); - } - - /// - /// Batch scripts are evil. - /// The escape character in batch is '^'. - /// - /// Example: - /// script.bat : echo %* - /// cmd.exe: script.bat foo^>out.txt - /// - /// This passes the argument "foo >out.txt" to script.bat. - /// Variable expansion happen before execution (i.e. it is preprocessing), so the script becomes: - /// - /// echo foo>out.txt - /// - /// which will write "foo" into the file "out.txt" - /// - /// To avoid this, one must call: - /// cmd.exe: script.bat foo^^^>out.txt - /// - /// which gets rewritten into: echo foo^>out.txt - /// and then executed. - /// - /// Note: Delayed expansion is not available for %*, %1 - /// set foo=%* and set foo="%*" with echo !foo! - /// will only move the command injection away from the "echo" to the "set" itself. - /// - private static string ShellEscape(string argLine) - { - var sb = new StringBuilder(); - foreach (var c in argLine) - { - // This escape is required after %* is expanded to prevent command injections - sb.Append('^'); - sb.Append('^'); - - // This escape is required only to pass the argument line to the batch script - sb.Append('^'); - sb.Append(c); - } - return sb.ToString(); - } - - #endregion Public properties - } -} diff --git a/src/CFamily/Subprocess/SubProcessFilePaths.cs b/src/CFamily/Subprocess/SubProcessFilePaths.cs deleted file mode 100644 index 2d23cbfd93..0000000000 --- a/src/CFamily/Subprocess/SubProcessFilePaths.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Diagnostics; -using System.IO; -using SonarLint.VisualStudio.Core.Helpers; - -namespace SonarLint.VisualStudio.CFamily.SubProcess -{ - /// - /// Returns file paths for various CFamily directories/files - /// - public static class SubProcessFilePaths - { - - static SubProcessFilePaths() - { - try - { - Directory.CreateDirectory(PchFileDirectory); - } - catch (Exception ex) - { - Debug.WriteLine($"PCH directory error: {ex.Message}"); - } - } - - private static string PchFileName = "PCH.preamble"; - private static string PchFileDirectory = PathHelper.GetTempDirForTask(true, "PCH"); - public static string WorkingDirectory => Path.GetTempPath(); - public static string PchFilePath => Path.Combine(PchFileDirectory, PchFileName); - public static string RequestConfigFilePath => Path.Combine(WorkingDirectory, "sonar-cfamily.request.reproducer"); - public static string ReproducerFilePath => Path.Combine(WorkingDirectory, "sonar-cfamily.reproducer"); - - private static readonly string CFamilyFilesDirectory = Path.Combine( - Path.GetDirectoryName(typeof(SubProcessFilePaths).Assembly.Location), - "lib"); - - public static readonly string AnalyzerExeFilePath = Path.Combine(CFamilyFilesDirectory, "subprocess.exe"); - } -} diff --git a/src/Core.UnitTests/RuleSettingsProviderTests.cs b/src/Core.UnitTests/RuleSettingsProviderTests.cs deleted file mode 100644 index 3a867ecdbd..0000000000 --- a/src/Core.UnitTests/RuleSettingsProviderTests.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.Core.UnitTests -{ - [TestClass] - public class RuleSettingsProviderTests - { - private readonly Language validLanguage = Language.C; - - private readonly RulesSettings standaloneSettings = new() - { - Rules = new Dictionary - { - {"standalone", new RuleConfig()} - } - }; - - private readonly RulesSettings connectedModeSettings = new() - { - Rules = new Dictionary - { - {"connected", new RuleConfig()} - } - }; - - [TestMethod] - public void Get_NotInConnectedMode_UserSettings() - { - var userSettingsProvider = CreateUserSettingsProvider(standaloneSettings); - var ruleSettingsSerializer = new Mock(); - - var testSubject = CreateTestSubject(BindingConfiguration.Standalone, userSettingsProvider.Object, ruleSettingsSerializer.Object); - - var result = testSubject.Get(); - - result.Should().Be(standaloneSettings); - - userSettingsProvider.VerifyGet(x=> x.UserSettings, Times.Once); - userSettingsProvider.VerifyNoOtherCalls(); - - ruleSettingsSerializer.Invocations.Count.Should().Be(0); - } - - [TestMethod] - public void Get_InConnectedMode_CantFindConnectModeFile_UserSettings() - { - var userSettingsProvider = CreateUserSettingsProvider(standaloneSettings); - var bindingConfiguration = GetConnectedModeConfiguration(); - var ruleSettingsSerializer = CreateRulesSettingsSerializer(bindingConfiguration, null); - - var testSubject = CreateTestSubject(bindingConfiguration, userSettingsProvider.Object, ruleSettingsSerializer.Object); - - var result = testSubject.Get(); - - result.Should().Be(standaloneSettings); - - userSettingsProvider.VerifyGet(x => x.UserSettings, Times.Once); - userSettingsProvider.VerifyNoOtherCalls(); - - ruleSettingsSerializer.Verify(x => x.SafeLoad(ConnectedModeFilePath(bindingConfiguration)), Times.Once); - ruleSettingsSerializer.VerifyNoOtherCalls(); - } - - [TestMethod] - public void Get_InConnectedMode_FoundConnectModeFile_ConnectedModeSettings() - { - var userSettingsProvider = new Mock(); - var bindingConfiguration = GetConnectedModeConfiguration(); - var ruleSettingsSerializer = CreateRulesSettingsSerializer(bindingConfiguration, connectedModeSettings); - - var testSubject = CreateTestSubject(bindingConfiguration, userSettingsProvider.Object, ruleSettingsSerializer.Object); - - var result = testSubject.Get(); - - result.Should().Be(connectedModeSettings); - - userSettingsProvider.Invocations.Count.Should().Be(0); - - ruleSettingsSerializer.Verify(x => x.SafeLoad(ConnectedModeFilePath(bindingConfiguration)), Times.Once); - ruleSettingsSerializer.VerifyNoOtherCalls(); - } - - private RuleSettingsProvider CreateTestSubject(BindingConfiguration configuration, - IUserSettingsProvider userSettingsProvider, - IRulesSettingsSerializer rulesSettingsSerializer) - { - var activeSolutionBoundTracker = new Mock(); - activeSolutionBoundTracker.Setup(x => x.CurrentConfiguration).Returns(configuration); - - return new RuleSettingsProvider(activeSolutionBoundTracker.Object, - userSettingsProvider, - rulesSettingsSerializer, - validLanguage, - Mock.Of()); - } - - private Mock CreateUserSettingsProvider(RulesSettings ruleSettings) - { - var userSettingsProvider = new Mock(); - userSettingsProvider.Setup(x => x.UserSettings).Returns(new UserSettings(ruleSettings)); - - return userSettingsProvider; - } - - private BindingConfiguration GetConnectedModeConfiguration() - { - return BindingConfiguration.CreateBoundConfiguration( - new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://localhost:2000"))), - SonarLintMode.Connected, - "some directory"); - } - - private Mock CreateRulesSettingsSerializer(BindingConfiguration bindingConfiguration, RulesSettings settings) - { - var serializer = new Mock(); - serializer.Setup(x => x.SafeLoad(ConnectedModeFilePath(bindingConfiguration))).Returns(settings); - - return serializer; - } - - private string ConnectedModeFilePath(BindingConfiguration bindingConfiguration) - { - return bindingConfiguration.BuildPathUnderConfigDirectory(validLanguage.FileSuffixAndExtension); - } - } -} diff --git a/src/Core/UserRuleSettings/IRuleSettingsProvider.cs b/src/Core/UserRuleSettings/IRuleSettingsProvider.cs deleted file mode 100644 index 61cd4f82b0..0000000000 --- a/src/Core/UserRuleSettings/IRuleSettingsProvider.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 SonarLint.VisualStudio.Core.Binding; - -namespace SonarLint.VisualStudio.Core.UserRuleSettings -{ - public interface IRuleSettingsProviderFactory - { - /// - /// Create for a specific language - /// - /// - /// In connected mode we need to know which file to load and we get it from . - /// - IRuleSettingsProvider Get(Language language); - } - - public interface IRuleSettingsProvider - { - /// - /// Get rule settings specified in connected mode, or in the settings.json file - /// - RulesSettings Get(); - } - - public class RuleSettingsProvider : IRuleSettingsProvider - { - private readonly IActiveSolutionBoundTracker activeSolutionBoundTracker; - private readonly IUserSettingsProvider userSettingsProvider; - private readonly IRulesSettingsSerializer serializer; - private readonly Language language; - private readonly ILogger logger; - - public RuleSettingsProvider(IActiveSolutionBoundTracker activeSolutionBoundTracker, - IUserSettingsProvider userSettingsProvider, - IRulesSettingsSerializer serializer, - Language language, - ILogger logger) - { - this.activeSolutionBoundTracker = activeSolutionBoundTracker; - this.userSettingsProvider = userSettingsProvider; - this.serializer = serializer; - this.language = language; - this.logger = logger; - } - - public RulesSettings Get() - { - RulesSettings settings = null; - - // If in connected mode, look for rule settings in the .sonarlint/sonarqube folder. - var binding = this.activeSolutionBoundTracker.CurrentConfiguration; - - if (binding != null && binding.Mode != SonarLintMode.Standalone) - { - settings = FindConnectedModeSettings(binding); - if (settings == null) - { - logger.WriteLine(CoreStrings.UnableToLoadConnectedModeSettings); - } - else - { - logger.WriteLine(CoreStrings.UsingConnectedModeSettings); - } - } - - // If we are not in connected mode or couldn't find the connected mode settings then fall back on the standalone settings. - settings = settings ?? userSettingsProvider.UserSettings.RulesSettings; - - return settings; - } - - private RulesSettings FindConnectedModeSettings(BindingConfiguration binding) - { - var filePath = binding.BuildPathUnderConfigDirectory(language.FileSuffixAndExtension); - var settings = serializer.SafeLoad(filePath); - - return settings; - } - } -} diff --git a/src/Integration.Vsix.UnitTests/Analysis/RuleSettingsProviderFactoryTests.cs b/src/Integration.Vsix.UnitTests/Analysis/RuleSettingsProviderFactoryTests.cs deleted file mode 100644 index 207fe12c9d..0000000000 --- a/src/Integration.Vsix.UnitTests/Analysis/RuleSettingsProviderFactoryTests.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.Integration.Vsix.Analysis; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Analysis -{ - [TestClass] - public class RuleSettingsProviderFactoryTests - { - [TestMethod] - public void MefCtor_CheckIsExported() - { - MefTestHelpers.CheckTypeCanBeImported( - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport()); - } - - [TestMethod] - public void Get_CreatesRuleSettingsProvider() - { - var testSubject = new RuleSettingsProviderFactory( - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of()); - - var result = testSubject.Get(Language.C); - - result.Should().NotBeNull(); - } - } -} diff --git a/src/Integration.Vsix.UnitTests/CFamily/CFamilyEmbeddedSonarWayRulesTests.cs b/src/Integration.Vsix.UnitTests/CFamily/CFamilyEmbeddedSonarWayRulesTests.cs deleted file mode 100644 index d4d8558543..0000000000 --- a/src/Integration.Vsix.UnitTests/CFamily/CFamilyEmbeddedSonarWayRulesTests.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 FluentAssertions.Execution; -using SonarLint.VisualStudio.CFamily; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.Integration.Vsix.CFamily.UnitTests -{ - [TestClass] - public class CFamilyEmbeddedSonarWayRulesTests - { - // Sanity checks that the rules metadata for the CFamily plugin is present and can be loaded - - // Note: how to find the expected number of active/inactive rules in SonarWay by language: - // 1. Start a local SQ instance with the correct plugin version installed - // 2. Browse to "Rules" e.g. http://localhost:9000/ - // or: - // SonarCloud: C: https://sonarcloud.io/organizations/sonarsource/quality_profiles/show?name=Sonar+way&language=c - // SonarCloud: C++: https://sonarcloud.io/organizations/sonarsource/quality_profiles/show?name=Sonar+way&language=cpp - // note: if you just look at the qp page, then it always shows 6 more inactive rules for c&cpp than available in the ide - // 3. Filter by Repository = SonarAnalyzer C - // 4. Filter by Quality Profile = Sonar way C - // The QP filter has "active/inactive" tabs. The number of rules is shown in the top-right of the screen. - // 5. Repeat for C++. - - // You can check the version of the plugin that is installed on the appropriate web API: - // e.g. https://next.sonarqube.com/sonarqube/api/plugins/installed and https://sonarcloud.io/api/plugins/installed - // Note - you need to be logged in. - - // Rule data for C-Family plugin v6.61.0.77816 - - private const int Active_C_Rules = 211; - private const int Inactive_C_Rules = 130; - - private const int Active_CPP_Rules = 453; - private const int Inactive_CPP_Rules = 219; - - private readonly CFamilySonarWayRulesConfigProvider rulesMetadataCache = new CFamilySonarWayRulesConfigProvider(CFamilyShared.CFamilyFilesDirectory); - - [TestMethod] - public void Read_Rules() - { - rulesMetadataCache.GetRulesConfiguration("c").AllPartialRuleKeys.Should().HaveCount(Active_C_Rules + Inactive_C_Rules); - rulesMetadataCache.GetRulesConfiguration("cpp").AllPartialRuleKeys.Should().HaveCount(Active_CPP_Rules + Inactive_CPP_Rules); - - // We don't currently support ObjC rules in VS - rulesMetadataCache.GetRulesConfiguration("objc").Should().BeNull(); - } - - [TestMethod] - public void Read_Active_Rules() - { - rulesMetadataCache.GetRulesConfiguration("c").ActivePartialRuleKeys.Should().HaveCount(Active_C_Rules); - rulesMetadataCache.GetRulesConfiguration("cpp").ActivePartialRuleKeys.Should().HaveCount(Active_CPP_Rules); - - // We don't currently support ObjC rules in VS - rulesMetadataCache.GetRulesConfiguration("objc").Should().BeNull(); - } - - [TestMethod] - public void Read_Rules_Params() - { - // The choice of rule ID here is arbitrary - any rule that has parameters will do. - rulesMetadataCache.GetRulesConfiguration("cpp").RulesParameters.TryGetValue("S100", out var parameters); - parameters.Should() - .Contain(new KeyValuePair("format", "^[a-z][a-zA-Z0-9]*$")); - } - - [TestMethod] - public void Read_Rules_Metadata() - { - // The choice of rule ID here is arbitrary - any rule will do - rulesMetadataCache.GetRulesConfiguration("cpp").RulesMetadata.TryGetValue("S100", out var metadata); - using (new AssertionScope()) - { - metadata.Type.Should().Be(IssueType.CodeSmell); - metadata.DefaultSeverity.Should().Be(IssueSeverity.Minor); - } - } - - [TestMethod] - [DataRow("S5536", "c")] - [DataRow("S5536", "cpp")] - public void CheckProjectLevelRule_IsDisabledByDefault(string ruleKey, string languageKey) - { - // The choice of rule ID here is arbitrary - any rule will do - rulesMetadataCache.GetRulesConfiguration(languageKey).AllPartialRuleKeys.Contains(ruleKey).Should().BeTrue(); - rulesMetadataCache.GetRulesConfiguration(languageKey).ActivePartialRuleKeys.Contains(ruleKey).Should().BeFalse(); - } - } -} diff --git a/src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueConverterFactoryTests.cs b/src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueConverterFactoryTests.cs deleted file mode 100644 index 49ff9d108e..0000000000 --- a/src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueConverterFactoryTests.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Utilities; -using Moq; -using SonarLint.VisualStudio.CFamily.Analysis; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Integration.Vsix.CFamily; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily -{ - [TestClass] - public class CFamilyIssueConverterFactoryTests - { - [TestMethod] - public void MefCtor_CheckIsExported() - { - MefTestHelpers.CheckTypeCanBeImported - (MefTestHelpers.CreateExport(Mock.Of()), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport()); - } - - [TestMethod] - public void Create_ReturnsNewInstance() - { - var textDocumentFactory = Mock.Of(); - var contentTypeRegistry = Mock.Of(); - var connectedModeFeaturesConfiguration = Mock.Of(); - - var testSubject = new CFamilyIssueConverterFactory(textDocumentFactory, contentTypeRegistry, connectedModeFeaturesConfiguration); - - // Create first item - var result1 = testSubject.Create(); - result1.Should().NotBeNull(); - - // Create second item - var result2 = testSubject.Create(); - result2.Should().NotBeNull(); - - result1.Should().NotBeSameAs(result2); - } - } -} diff --git a/src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueToAnalysisIssueConverterTests.cs b/src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueToAnalysisIssueConverterTests.cs deleted file mode 100644 index 2bd53d3259..0000000000 --- a/src/Integration.Vsix.UnitTests/CFamily/CFamilyIssueToAnalysisIssueConverterTests.cs +++ /dev/null @@ -1,840 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.IO.Abstractions; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Utilities; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.CFamily.SubProcess; -using SonarLint.VisualStudio.Core.Analysis; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.Infrastructure.VS.Editor; -using SonarLint.VisualStudio.Integration.Vsix.CFamily; -using Edit = SonarLint.VisualStudio.CFamily.SubProcess.Edit; - -namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily -{ - [TestClass] - public class CFamilyIssueToAnalysisIssueConverterTests - { - private static readonly IContentType DummyContentType = Mock.Of(); - - [TestMethod] - public void Convert_NoMessageParts_IssueWithoutFlows() - { - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, new MessagePart[0], Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.Flows.Should().BeEmpty(); - } - - [TestMethod] - public void Convert_HasMessageParts_IssueWithSingleFlowAndLocations() - { - var messageParts = new List - { - new MessagePart("c:\\test1.cpp", 1, 2, 3, 4, "this is a test 1"), - new MessagePart("c:\\test2.cpp", 5, 6, 7, 8, "this is a test 2") - }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, messageParts.ToArray(), Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - var expectedLocations = new List - { - new AnalysisIssueLocation("this is a test 1", "c:\\test1.cpp", new TextRange(1, 3, 1, 3, null)), - new AnalysisIssueLocation("this is a test 2", "c:\\test2.cpp", new TextRange(5, 7, 5, 7, null)), - }; - - var expectedFlows = new List - { - new AnalysisIssueFlow(expectedLocations) - }; - - issue.Flows.Count.Should().Be(1); - issue.Flows.Should().BeEquivalentTo(expectedFlows, config => config.WithStrictOrdering()); - } - - [TestMethod] - public void Convert_HasMessagePartsMakeFlow_FlowsAreReversed() - { - var messageParts = new List - { - new MessagePart("c:\\test1.cpp", 1, 2, 3, 4, "this is a test 1"), - new MessagePart("c:\\test2.cpp", 5, 6, 7, 8, "this is a test 2") - }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", true, messageParts.ToArray(), Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - var expectedLocations = new List - { - new AnalysisIssueLocation("this is a test 2", "c:\\test2.cpp", new TextRange(5, 7, 5, 7, null)), - new AnalysisIssueLocation("this is a test 1", "c:\\test1.cpp", new TextRange(1, 3, 1, 3, null)), - }; - - var expectedFlows = new List - { - new AnalysisIssueFlow(expectedLocations) - }; - - issue.Flows.Count.Should().Be(1); - issue.Flows.Should().BeEquivalentTo(expectedFlows, config => config.WithStrictOrdering()); - } - - [TestMethod] - public void Convert_IssueEndLineIsNotZero_OffsetsAreCalculatedCorrectly() - { - var message = new Message("rule2", "file", 4, 3, 2, 1, "test endline is not zero", false, new MessagePart[0], Array.Empty()); - - // Act - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - // Assert - issue.PrimaryLocation.TextRange.StartLine.Should().Be(4); - issue.PrimaryLocation.TextRange.StartLineOffset.Should().Be(3 - 1); - - issue.PrimaryLocation.TextRange.EndLine.Should().Be(2); - issue.PrimaryLocation.TextRange.EndLineOffset.Should().Be(1 - 1); - } - - [TestMethod] - public void Convert_IssueEndLineIsZero_OffsetsAreIgnored() - { - // Special case: ignore column offsets if EndLine is zero - var message = new Message("rule2", "ff", 101, 1, 0, 3, "test endline is zero", true, new MessagePart[0], Array.Empty()); - - // Act - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - // Assert - issue.PrimaryLocation.TextRange.StartLine.Should().Be(101); - issue.PrimaryLocation.TextRange.StartLineOffset.Should().Be(0); - - issue.PrimaryLocation.TextRange.EndLine.Should().Be(0); - issue.PrimaryLocation.TextRange.EndLineOffset.Should().Be(0); - } - - [TestMethod] - public void Convert_LocationEndLineIsNotZero_OffsetsAreCalculatedCorrectly() - { - var messagePart = new MessagePart("file", 10, 2, 30, 4, "text"); - var message = new Message("rule2", "file", 4, 3, 2, 1, "test endline is not zero", false, new[] { messagePart }, Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - var convertedLocation = issue.Flows.First().Locations.First(); - - convertedLocation.TextRange.StartLine.Should().Be(10); - convertedLocation.TextRange.StartLineOffset.Should().Be(1); - - convertedLocation.TextRange.EndLine.Should().Be(30); - convertedLocation.TextRange.EndLineOffset.Should().Be(3); - } - - [TestMethod] - public void Convert_LocationEndLineIsZero_OffsetsAreIgnored() - { - var messagePart = new MessagePart("file", 10, 2, 0, 4, "text"); - var message = new Message("rule2", "file", 4, 3, 2, 1, "test endline is not zero", false, new[] { messagePart }, Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - var convertedLocation = issue.Flows.First().Locations.First(); - - convertedLocation.TextRange.StartLine.Should().Be(10); - convertedLocation.TextRange.StartLineOffset.Should().Be(0); - - convertedLocation.TextRange.EndLine.Should().Be(0); - convertedLocation.TextRange.EndLineOffset.Should().Be(0); - } - - [TestMethod] - public void Convert_NoMessageParts_LineHashCalculatedForIssueOnly() - { - const string filePath = "file1.cpp"; - const int line = 10; - - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - var issueHash = SetupLineHash(fileSystemMock, lineHashCalculator, textDocumentFactoryService, filePath, line); - - var message = new Message("rule2", filePath, line, 3, 2, 1, "this is a test", false, new MessagePart[0], Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - var issue = Convert(testSubject, message); - - issue.PrimaryLocation.TextRange.LineHash.Should().Be(issueHash); - } - - [TestMethod] - public void Convert_HasMessageParts_LineHashCalculatedForIssueAndLocations() - { - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - const string issueFilePath = "file1.cpp"; - const int issueLine = 10; - var issueHash = SetupLineHash(fileSystemMock, lineHashCalculator, textDocumentFactoryService, issueFilePath, issueLine); - - const string firstLocationPath = "file2.cpp"; - const int firstLocationLine = 20; - var firstLocationHash = SetupLineHash(fileSystemMock, lineHashCalculator, textDocumentFactoryService, firstLocationPath, firstLocationLine); - - const string secondLocationPath = "file3.cpp"; - const int secondLocationLine = 30; - var secondLocationHash = SetupLineHash(fileSystemMock, lineHashCalculator, textDocumentFactoryService, secondLocationPath, secondLocationLine); - - var messageParts = new List - { - new MessagePart(firstLocationPath, firstLocationLine, 2, 3, 4, "this is a test 1"), - new MessagePart(secondLocationPath, secondLocationLine, 6, 7, 8, "this is a test 2") - }; - - var message = new Message("rule2", issueFilePath, issueLine, 3, 2, 1, "this is a test", false, messageParts.ToArray(), Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - var issue = Convert(testSubject, message); - - issue.PrimaryLocation.TextRange.LineHash.Should().Be(issueHash); - - var firstLocation = issue.Flows[0].Locations[0]; - var secondLocation = issue.Flows[0].Locations[1]; - - secondLocation.TextRange.LineHash.Should().Be(secondLocationHash); - firstLocation.TextRange.LineHash.Should().Be(firstLocationHash); - } - - [TestMethod] - public void Convert_HasMessageParts_LineHashCalculatedForNonFileLevelLocationsOnly() - { - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - const string nonFileLevelLocationFilePath = "file2.cpp"; - const int nonFileLevelLocationLine = 20; - var nonFileLevelLocationHash = SetupLineHash(fileSystemMock, lineHashCalculator, textDocumentFactoryService, nonFileLevelLocationFilePath, nonFileLevelLocationLine); - - var messageParts = new List - { - new MessagePart(nonFileLevelLocationFilePath, nonFileLevelLocationLine, 2, 3, 4, "this is a test 1"), - new MessagePart("file3.cpp", 1, 1, 0, 0, "this is a test 2") - }; - - var fileLevelIssue = new Message("rule2", "file1.pp", 1, 0, 0, 0, "this is a test", false, messageParts.ToArray(), Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - var issue = Convert(testSubject, fileLevelIssue); - - issue.PrimaryLocation.TextRange.LineHash.Should().BeNull(); - - var nonFileLevelLocation = issue.Flows[0].Locations[0]; - var fileLevelLocation = issue.Flows[0].Locations[1]; - - fileLevelLocation.TextRange.LineHash.Should().BeNull(); - nonFileLevelLocation.TextRange.LineHash.Should().Be(nonFileLevelLocationHash); - } - - [TestMethod] - public void Convert_FileDoesNotExist_NullLineHash() - { - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - const string existingFilePath = "file2.cpp"; - const int line = 20; - var expectedHash = SetupLineHash(fileSystemMock, lineHashCalculator, textDocumentFactoryService, existingFilePath, line); - - var messageParts = new List - { - new MessagePart(existingFilePath, line, 2, 3, 4, "this is a test 1"), - new MessagePart("non existing path", 2, 6, 7, 8, "this is a test 2") - }; - - var message = new Message("rule2", "non existing path", 3, 3, 2, 1, "this is a test", false, messageParts.ToArray(), Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - var issue = Convert(testSubject, message); - issue.PrimaryLocation.TextRange.LineHash.Should().BeNull(); - - var firstLocation = issue.Flows[0].Locations[0]; - var secondLocation = issue.Flows[0].Locations[1]; - - secondLocation.TextRange.LineHash.Should().BeNull(); - firstLocation.TextRange.LineHash.Should().Be(expectedHash); - - // verify that the mock was called only for firstLocationPath - textDocumentFactoryService.Verify(x => x.CreateAndLoadTextDocument(existingFilePath, DummyContentType), Times.Once); - textDocumentFactoryService.Verify(x => x.CreateAndLoadTextDocument(It.IsAny(), It.IsAny()), Times.Once); - } - - [TestMethod] - public void Convert_ExistingFile_NoTextDocument_NullLineHash() - { - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - const string filePath = "test.cpp"; - fileSystemMock.Setup(x => x.File.Exists(filePath)).Returns(true); - - SetupDocumentLoad(textDocumentFactoryService, filePath, textDocument: null); - - var message = new Message("rule2", filePath, 3, 3, 2, 1, "this is a test", false, Array.Empty(), Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - - var issue = Convert(testSubject, message); - issue.PrimaryLocation.TextRange.LineHash.Should().BeNull(); - lineHashCalculator.Invocations.Should().BeEmpty(); - } - - [TestMethod] - public void Convert_ExistingFile_NoTextBuffer_NullLineHash() - { - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - const string filePath = "test.cpp"; - fileSystemMock.Setup(x => x.File.Exists(filePath)).Returns(true); - - var textDocument = CreateTextDocument(null); - SetupDocumentLoad(textDocumentFactoryService, filePath, textDocument); - - var message = new Message("rule2", filePath, 3, 3, 2, 1, "this is a test", false, Array.Empty(), Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - - var issue = Convert(testSubject, message); - issue.PrimaryLocation.TextRange.LineHash.Should().BeNull(); - lineHashCalculator.Invocations.Should().BeEmpty(); - } - - [TestMethod] - public void Convert_ExistingFile_NoTextSnapshot_NullLineHash() - { - var fileSystemMock = CreateFileSystemMock(); - var lineHashCalculator = new Mock(); - var textDocumentFactoryService = new Mock(); - - const string filePath = "test.cpp"; - fileSystemMock.Setup(x => x.File.Exists(filePath)).Returns(true); - - var textBuffer = CreateTextBuffer(null); - var textDocument = CreateTextDocument(textBuffer); - SetupDocumentLoad(textDocumentFactoryService, filePath, textDocument); - - var message = new Message("rule2", filePath, 3, 3, 2, 1, "this is a test", false, Array.Empty(), Array.Empty()); - - var testSubject = CreateTestSubject(lineHashCalculator.Object, fileSystemMock.Object, textDocumentFactoryService.Object); - - var issue = Convert(testSubject, message); - issue.PrimaryLocation.TextRange.LineHash.Should().BeNull(); - lineHashCalculator.Invocations.Should().BeEmpty(); - } - - [TestMethod] - public void Convert_HasMessageParts_EachFileIsLoadedOnlyOnce_PerCall() - { - var messageParts = new List - { - new MessagePart("file1.cpp", 10, 2, 3, 4, "this is a test 1"), - new MessagePart("file1.cpp", 20, 6, 7, 8, "this is a test 2"), - new MessagePart("file2.cpp", 30, 6, 7, 8, "this is a test 2") - }; - - var message = new Message("rule2", "file2.cpp", 40, 3, 2, 1, "this is a test", false, messageParts.ToArray(), Array.Empty()); - - var fileSystemMock = CreateFileSystemMock(fileExists: true); - var textDocFactory = new Mock(); - - var testSubject = CreateTestSubject(fileSystem: fileSystemMock.Object, textDocumentFactoryService: textDocFactory.Object); - - Convert(testSubject, message); - - fileSystemMock.Verify(x => x.File.Exists("file1.cpp"), Times.Once); - fileSystemMock.Verify(x => x.File.Exists("file2.cpp"), Times.Once); - fileSystemMock.VerifyNoOtherCalls(); - - textDocFactory.Verify(x => x.CreateAndLoadTextDocument("file1.cpp", It.IsAny()), Times.Once); - textDocFactory.Verify(x => x.CreateAndLoadTextDocument("file2.cpp", It.IsAny()), Times.Once); - textDocFactory.VerifyNoOtherCalls(); - } - - [TestMethod] - public void Convert_HasMessageParts_EachFileIsLoadedOnlyOnce_AcrossMultipleCalls() - { - var messageParts1 = new List - { - new MessagePart("file1.cpp", 10, 2, 3, 4, "this is a test 1"), - new MessagePart("file2.cpp", 30, 6, 7, 8, "this is a test 2") - }; - var message1 = new Message("rule2", "file2.cpp", 40, 3, 2, 1, "this is a test", false, messageParts1.ToArray(), Array.Empty()); - - var messageParts2 = new List - { - new MessagePart("FILE1.cpp", 100, 2, 3, 4, "this is a test 1"), - new MessagePart("FILE2.cpp", 300, 6, 7, 8, "this is a test 2") - }; - var message2 = new Message("rule2", "file2.cpp", 40, 3, 2, 1, "this is another test", false, messageParts2.ToArray(), Array.Empty()); - - var fileSystemMock = CreateFileSystemMock(fileExists: true); - var textDocFactory = new Mock(); - - var testSubject = CreateTestSubject(fileSystem: fileSystemMock.Object, textDocumentFactoryService: textDocFactory.Object); - - // Convert multiple times - Convert(testSubject, message1); - Convert(testSubject, message2); - - fileSystemMock.Verify(x => x.File.Exists("file1.cpp"), Times.Once); - fileSystemMock.Verify(x => x.File.Exists("file2.cpp"), Times.Once); - fileSystemMock.VerifyNoOtherCalls(); - - textDocFactory.Verify(x => x.CreateAndLoadTextDocument("file1.cpp", It.IsAny()), Times.Once); - textDocFactory.Verify(x => x.CreateAndLoadTextDocument("file2.cpp", It.IsAny()), Times.Once); - textDocFactory.VerifyNoOtherCalls(); - } - - [TestMethod] - [DataRow("rule2", AnalysisIssueSeverity.Info, AnalysisIssueType.CodeSmell)] - [DataRow("rule3", AnalysisIssueSeverity.Critical, AnalysisIssueType.Vulnerability)] - public void Convert_SeverityAndTypeLookup(string ruleKey, AnalysisIssueSeverity severity, AnalysisIssueType type) - { - var message = new Message(ruleKey, "any", 4, 3, 2, 1, "message", false, new MessagePart[0], Array.Empty()); - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.Id.Should().BeNull(); - issue.RuleKey.Should().Be($"lang1:{ruleKey}"); - issue.Severity.Should().Be(severity); - issue.Type.Should().Be(type); - } - - [TestMethod] - [DataRow(true, IssueType.Bug, SoftwareQualitySeverity.High)] - [DataRow(false, IssueType.Bug, null)] - [DataRow(true, IssueType.SecurityHotspot, null)] - public void Convert_NewCCTEnabled_FillsSoftwareQualitySeverity(bool isCCTEnabled, IssueType type, SoftwareQualitySeverity? expectedSoftwareQualitySeverity) - { - var message = new Message("key", "any", 4, 3, 2, 1, "message", false, new MessagePart[0], Array.Empty()); - - var impacts = new Dictionary - { - { SoftwareQuality.Maintainability, SoftwareQualitySeverity.High } - }; - - var ruleMetaData = CreateRuleMetaData(impacts, type); - var rulesMetaData = new Dictionary - { - {"key", ruleMetaData } - }; - - var CFamilyconfig = new Mock(); - CFamilyconfig.Setup(c => c.RulesMetadata).Returns(rulesMetaData); - - var CMConfig = new Mock(); - CMConfig.Setup(c => c.IsNewCctAvailable()).Returns(isCCTEnabled); - - var testSubject = CreateTestSubject(connectedModeFeaturesConfiguration: CMConfig.Object); - var issue = testSubject.Convert(message, "lang", CFamilyconfig.Object); - - var highestSoftwareQualitySeverity = issue.HighestImpact?.Severity; - highestSoftwareQualitySeverity.Should().Be(expectedSoftwareQualitySeverity); - } - - [TestMethod] - [Microsoft.VisualStudio.TestTools.UnitTesting.Description("Regression test for https://github.com/SonarSource/sonarlint-visualstudio/issues/2149")] - [DataRow("", "")] // empty should not throw - [DataRow("a.txt", "a.txt")] // not-rooted should stay the same - [DataRow("c:\\a.txt", "c:\\a.txt")] - [DataRow("c:/a.txt", "c:\\a.txt")] - [DataRow("c:/a/b/c.txt", "c:\\a\\b\\c.txt")] - [DataRow("c:/a\\b/c.txt", "c:\\a\\b\\c.txt")] - public void Convert_HasMessageParts_QualifiedFilePath(string originalPath, string expectedPath) - { - var messageParts = new List - { - new MessagePart(originalPath, 10, 2, 3, 4, "this is a test 1"), - }; - - var message = new Message("rule2", "file2.cpp", 40, 3, 2, 1, "this is a test", false, messageParts.ToArray(), Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.Flows[0].Locations[0].FilePath.Should().Be(expectedPath); - } - - [TestMethod] - [Microsoft.VisualStudio.TestTools.UnitTesting.Description("Regression test for https://github.com/SonarSource/sonarlint-visualstudio/issues/2557")] - [DataRow("", "")] // empty should not throw - [DataRow("a.txt", "a.txt")] // not-rooted should stay the same - [DataRow("c:\\a.txt", "c:\\a.txt")] - [DataRow("c:/a.txt", "c:\\a.txt")] - [DataRow("c:/a/b/c.txt", "c:\\a\\b\\c.txt")] - [DataRow("c:/a\\b/c.txt", "c:\\a\\b\\c.txt")] - public void Convert_FilePath_QualifiedFilePath(string originalPath, string expectedPath) - { - var message = new Message("rule2", originalPath, 40, 3, 2, 1, "this is a test", false, Array.Empty(), Array.Empty()); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.PrimaryLocation.FilePath.Should().Be(expectedPath); - } - - [TestMethod] - [DataRow(IssueSeverity.Blocker, AnalysisIssueSeverity.Blocker)] - [DataRow(IssueSeverity.Critical, AnalysisIssueSeverity.Critical)] - [DataRow(IssueSeverity.Info, AnalysisIssueSeverity.Info)] - [DataRow(IssueSeverity.Major, AnalysisIssueSeverity.Major)] - [DataRow(IssueSeverity.Minor, AnalysisIssueSeverity.Minor)] - public void ConvertFromIssueSeverity(IssueSeverity cfamilySeverity, AnalysisIssueSeverity analysisIssueSeverity) - { - CFamilyIssueToAnalysisIssueConverter.Convert(cfamilySeverity).Should().Be(analysisIssueSeverity); - } - - [TestMethod] - public void ConvertFromIssueSeverity_InvalidValue_Throws() - { - Action act = () => CFamilyIssueToAnalysisIssueConverter.Convert((IssueSeverity)(-1)); - act.Should().ThrowExactly().And.ParamName.Should().Be("issueSeverity"); - } - - [TestMethod] - [DataRow(IssueType.Bug, AnalysisIssueType.Bug)] - [DataRow(IssueType.CodeSmell, AnalysisIssueType.CodeSmell)] - [DataRow(IssueType.Vulnerability, AnalysisIssueType.Vulnerability)] - [DataRow(IssueType.SecurityHotspot, AnalysisIssueType.SecurityHotspot)] - public void ConvertFromIssueType(IssueType cfamilyIssueType, AnalysisIssueType analysisIssueType) - { - CFamilyIssueToAnalysisIssueConverter.Convert(cfamilyIssueType).Should().Be(analysisIssueType); - } - - [TestMethod] - [DataRow(SoftwareQualitySeverity.High, SoftwareQualitySeverity.High)] - [DataRow(SoftwareQualitySeverity.Medium, SoftwareQualitySeverity.Medium)] - [DataRow(SoftwareQualitySeverity.Low, SoftwareQualitySeverity.Low)] - [DataRow(null, null)] - public void GetHighestImpact_ReturnsImpactWithHighestSeverity(SoftwareQualitySeverity? softwareQualitySeverity, SoftwareQualitySeverity? expectedHighestSoftwareQualitySeverity) - { - var impacts = new Dictionary(); - if (softwareQualitySeverity.HasValue) - { - impacts.Add(SoftwareQuality.Maintainability, softwareQualitySeverity.Value); - } - RuleMetadata ruleMetaData = CreateRuleMetaData(impacts); - - var highestSoftwareQualitySeverity = CFamilyIssueToAnalysisIssueConverter.GetHighestImpact(ruleMetaData)?.Severity; - - highestSoftwareQualitySeverity.Should().Be(expectedHighestSoftwareQualitySeverity); - } - - [TestMethod] - [DataRow(new SoftwareQualitySeverity[] { SoftwareQualitySeverity.Low, SoftwareQualitySeverity.Medium }, SoftwareQualitySeverity.Medium)] - [DataRow(new SoftwareQualitySeverity[] { SoftwareQualitySeverity.Low, SoftwareQualitySeverity.High }, SoftwareQualitySeverity.High)] - [DataRow(new SoftwareQualitySeverity[] { SoftwareQualitySeverity.Medium, SoftwareQualitySeverity.High }, SoftwareQualitySeverity.High)] - public void GetHighestImpact_HasTwoImpacts_GetsTheHighestOne(SoftwareQualitySeverity[] softwareQualitySeverities, SoftwareQualitySeverity? expectedHighestSoftwareQualitySeverity) - { - var impacts = new Dictionary - { - { SoftwareQuality.Maintainability, softwareQualitySeverities[0] }, - { SoftwareQuality.Reliability, softwareQualitySeverities[1] } - }; - RuleMetadata ruleMetaData = CreateRuleMetaData(impacts); - - var highestSoftwareQualitySeverity = CFamilyIssueToAnalysisIssueConverter.GetHighestImpact(ruleMetaData)?.Severity; - - highestSoftwareQualitySeverity.Should().Be(expectedHighestSoftwareQualitySeverity); - } - - [TestMethod] - public void GetHighestImpact_HasThreeImpacts_GetsTheHighestOne() - { - var impacts = new Dictionary - { - { SoftwareQuality.Maintainability, SoftwareQualitySeverity.Low }, - { SoftwareQuality.Reliability, SoftwareQualitySeverity.High }, - { SoftwareQuality.Security, SoftwareQualitySeverity.Medium } - }; - RuleMetadata ruleMetaData = CreateRuleMetaData(impacts); - - var highestImpact = CFamilyIssueToAnalysisIssueConverter.GetHighestImpact(ruleMetaData); - - highestImpact.Severity.Should().Be(SoftwareQualitySeverity.High); - highestImpact.Quality.Should().Be(SoftwareQuality.Reliability); - } - - [TestMethod] - [DataRow(SoftwareQualitySeverity.Blocker)] - [DataRow(SoftwareQualitySeverity.High)] - [DataRow(SoftwareQualitySeverity.Medium)] - [DataRow(SoftwareQualitySeverity.Low)] - [DataRow(SoftwareQualitySeverity.Info)] - public void GetHighestImpact_HasTwoHighImpactsForDifferentQualities_GetsTheHighestSoftwareQuality(SoftwareQualitySeverity softwareQualitySeverity) - { - var impacts = new Dictionary - { - { SoftwareQuality.Maintainability, SoftwareQualitySeverity.Info }, - { SoftwareQuality.Reliability, softwareQualitySeverity }, - { SoftwareQuality.Security, softwareQualitySeverity } - }; - RuleMetadata ruleMetaData = CreateRuleMetaData(impacts); - - var highestImpact = CFamilyIssueToAnalysisIssueConverter.GetHighestImpact(ruleMetaData); - - highestImpact.Severity.Should().Be(softwareQualitySeverity); - highestImpact.Quality.Should().Be(SoftwareQuality.Security); - } - - [TestMethod] - public void ConvertFromIssueType_InvalidValue_Throws() - - { - Action act = () => CFamilyIssueToAnalysisIssueConverter.Convert((IssueType)(-1)); - act.Should().ThrowExactly().And.ParamName.Should().Be("issueType"); - } - - [TestMethod] - public void Convert_Issue_WithSingleFixSingleEdit() - { - var fix1 = new Fix("Fix 1", new Edit[] { new Edit(1, 2, 3, 4, "Edit 1") }); - - var fixes = new Fix[] { fix1 }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, new MessagePart[0], fixes); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.Fixes.Count.Should().Be(1); - - CompareFixes(fix1, issue.Fixes[0]); - } - - [TestMethod] - public void Convert_Issue_WithSingleFixMultipleEdits() - { - var fix1 = new Fix("Fix 1", new Edit[] { new Edit(11, 12, 13, 14, "Edit 1"), new Edit(21, 22, 23, 24, "Edit 2"), new Edit(31, 32, 33, 34, "Edit 3"), new Edit(41, 42, 43, 44, "Edit 4") }); - - var fixes = new[] { fix1 }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, new MessagePart[0], fixes); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.Fixes.Count.Should().Be(1); - - CompareFixes(fix1, issue.Fixes[0]); - } - - [TestMethod] - public void Convert_Issue_WithMultipleFixesMultipleEdits() - { - var fix1 = new Fix("Fix 1", new Edit[] { new Edit(11, 12, 13, 14, "Edit 1"), new Edit(21, 22, 23, 24, "Edit 2"), new Edit(31, 32, 33, 34, "Edit 3"), new Edit(41, 42, 43, 44, "Edit 4") }); - var fix2 = new Fix("Fix 2", new Edit[] { new Edit(51, 52, 53, 54, "Edit 5"), new Edit(61, 62, 63, 64, "Edit 6"), new Edit(71, 72, 73, 74, "Edit 7"), new Edit(81, 82, 83, 84, "Edit 8") }); - - var fixes = new Fix[] { fix1, fix2 }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, new MessagePart[0], fixes); - - var testSubject = CreateTestSubject(); - var issue = Convert(testSubject, message); - - issue.Fixes.Count.Should().Be(2); - - CompareFixes(fix1, issue.Fixes[0]); - CompareFixes(fix2, issue.Fixes[1]); - } - - [TestMethod] - public void Convert_Issue_WithSingleFixNullEdit_Throws() - { - var fix1 = new Fix("Fix 1", null); - - var fixes = new Fix[] { fix1 }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, new MessagePart[0], fixes); - - var testSubject = CreateTestSubject(); - Action act = () => Convert(testSubject, message); - - act.Should().ThrowExactly().And.ParamName.Should().Be("edits"); - } - - [TestMethod] - public void Convert_Issue_WithSingleFixEmptyEdit_Throws() - { - var fix1 = new Fix("Fix 1", Array.Empty()); - - var fixes = new Fix[] { fix1 }; - - var message = new Message("rule2", "file", 4, 3, 2, 1, "this is a test", false, new MessagePart[0], fixes); - - var testSubject = CreateTestSubject(); - Action act = () => Convert(testSubject, message); - - act.Should().ThrowExactly().And.ParamName.Should().Be("edits"); - } - - private static void CompareFixes(Fix fix, IQuickFix quickFix) - { - quickFix.Edits.Count.Should().Be(fix.Edits.Length, $"because number of edits were not equal in {fix.Message}"); - - for (int i = 0; i < fix.Edits.Length; i++) - { - fix.Edits[i].StartColumn.Should().Be(quickFix.Edits[i].RangeToReplace.StartLineOffset + 1, $"because StartColumn was not equal in {fix.Message}, edit: {i} "); - fix.Edits[i].EndColumn.Should().Be(quickFix.Edits[i].RangeToReplace.EndLineOffset + 1, $"because EndColumn was not equal in {fix.Message}, edit: {i} "); - fix.Edits[i].StartLine.Should().Be(quickFix.Edits[i].RangeToReplace.StartLine, $"because StartLine was not equal in {fix.Message}, edit: {i} "); - fix.Edits[i].EndLine.Should().Be(quickFix.Edits[i].RangeToReplace.EndLine, $"because EndLine was not equal in {fix.Message}, edit: {i} "); - fix.Edits[i].Text.Should().Be(quickFix.Edits[i].NewText, $"because NewText was not equal in {fix.Message}, edit: {i} "); - } - } - - private static ICFamilyRulesConfig GetDummyRulesConfiguration() - { - var config = new Mock(); - config.Setup(x => x.LanguageKey).Returns("any"); - - var keyToMetadataMap = new Dictionary - { - { "rule1", new RuleMetadata { DefaultSeverity = IssueSeverity.Blocker, Type = IssueType.Bug } }, - { "rule2", new RuleMetadata { DefaultSeverity = IssueSeverity.Info, Type = IssueType.CodeSmell } }, - { "rule3", new RuleMetadata { DefaultSeverity = IssueSeverity.Critical, Type = IssueType.Vulnerability} }, - }; - - config.Setup(x => x.RulesMetadata).Returns(keyToMetadataMap); - - return config.Object; - } - - private static string SetupLineHash(Mock fileSystemMock, - Mock lineHashCalculatorMock, - Mock textDocumentFactoryService, - string filePath, - int line) - { - fileSystemMock.Setup(x => x.File.Exists(filePath)).Returns(true); - - var textSnapshot = Mock.Of(); - var textBuffer = CreateTextBuffer(textSnapshot); - var textDocument = CreateTextDocument(textBuffer); - - SetupDocumentLoad(textDocumentFactoryService, filePath, textDocument); - - var hash = Guid.NewGuid().ToString(); - - lineHashCalculatorMock.Setup(x => x.Calculate(textSnapshot, line)).Returns(hash); - - return hash; - } - - private static IAnalysisIssue Convert(CFamilyIssueToAnalysisIssueConverter testSubject, Message message) => - testSubject.Convert(message, "lang1", GetDummyRulesConfiguration()); - - private static CFamilyIssueToAnalysisIssueConverter CreateTestSubject( - ILineHashCalculator lineHashCalculator = null, - IFileSystem fileSystem = null, - ITextDocumentFactoryService textDocumentFactoryService = null, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration = null) - { - var contentTypeRegistryService = CreateContentTypeRegistryService(); - - lineHashCalculator ??= Mock.Of(); - fileSystem ??= CreateFileSystemMock().Object; - textDocumentFactoryService ??= Mock.Of(); - - if (connectedModeFeaturesConfiguration == null) - { - var connectedModeFeaturesConfigurationMock = new Mock(); - connectedModeFeaturesConfigurationMock.Setup(c => c.IsNewCctAvailable()).Returns(false); - - connectedModeFeaturesConfiguration = connectedModeFeaturesConfigurationMock.Object; - } - - var testSubject = new CFamilyIssueToAnalysisIssueConverter(textDocumentFactoryService, contentTypeRegistryService, connectedModeFeaturesConfiguration, lineHashCalculator, fileSystem); - - return testSubject; - } - - private static Mock CreateFileSystemMock(bool fileExists = false) - { - var fileSystemMock = new Mock(); - fileSystemMock.Setup(x => x.File.Exists(It.IsAny())).Returns(fileExists); - - return fileSystemMock; - } - - private static IContentTypeRegistryService CreateContentTypeRegistryService() - { - var contentTypeRegistryService = new Mock(); - contentTypeRegistryService.Setup(x => x.UnknownContentType).Returns(DummyContentType); - - return contentTypeRegistryService.Object; - } - - private static void SetupDocumentLoad(Mock textDocumentFactoryService, string filePath, ITextDocument textDocument) - { - textDocumentFactoryService - .Setup(x => x.CreateAndLoadTextDocument(filePath, DummyContentType)) - .Returns(textDocument); - } - - private static ITextDocument CreateTextDocument(ITextBuffer textBuffer) - { - var textDocument = new Mock(); - textDocument.Setup(x => x.TextBuffer).Returns(textBuffer); - - return textDocument.Object; - } - - private static ITextBuffer CreateTextBuffer(ITextSnapshot textSnapshot) - { - var textBuffer = new Mock(); - textBuffer.Setup(x => x.CurrentSnapshot).Returns(textSnapshot); - - return textBuffer.Object; - } - - private static RuleMetadata CreateRuleMetaData(Dictionary impacts, IssueType type = default) - { - var code = new Code { Impacts = impacts }; - var ruleMetaData = new RuleMetadata { Code = code, Type = type }; - return ruleMetaData; - } - } -} diff --git a/src/Integration.Vsix/Analysis/RuleSettingsProviderFactory.cs b/src/Integration.Vsix/Analysis/RuleSettingsProviderFactory.cs deleted file mode 100644 index ce4beff15a..0000000000 --- a/src/Integration.Vsix/Analysis/RuleSettingsProviderFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.ComponentModel.Composition; -using System.IO.Abstractions; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Core.UserRuleSettings; - -namespace SonarLint.VisualStudio.Integration.Vsix.Analysis -{ - [Export(typeof(IRuleSettingsProviderFactory))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal class RuleSettingsProviderFactory : IRuleSettingsProviderFactory - { - private readonly IActiveSolutionBoundTracker activeSolutionBoundTracker; - private readonly IUserSettingsProvider userSettingsProvider; - private readonly IRulesSettingsSerializer serializer; - private readonly ILogger logger; - - [ImportingConstructor] - public RuleSettingsProviderFactory(IActiveSolutionBoundTracker activeSolutionBoundTracker, - IUserSettingsProvider userSettingsProvider, - ILogger logger) - : this(activeSolutionBoundTracker, - userSettingsProvider, - new RulesSettingsSerializer(new FileSystem(), logger), - logger) - { - } - - internal RuleSettingsProviderFactory(IActiveSolutionBoundTracker activeSolutionBoundTracker, - IUserSettingsProvider userSettingsProvider, - IRulesSettingsSerializer serializer, - ILogger logger) - { - this.activeSolutionBoundTracker = activeSolutionBoundTracker; - this.userSettingsProvider = userSettingsProvider; - this.serializer = serializer; - this.logger = logger; - } - - public IRuleSettingsProvider Get(Language language) => - new RuleSettingsProvider(activeSolutionBoundTracker, userSettingsProvider, serializer, language, logger); - } -} diff --git a/src/Integration.Vsix/CFamily/CFamilyIssueToAnalysisIssueConverter.cs b/src/Integration.Vsix/CFamily/CFamilyIssueToAnalysisIssueConverter.cs deleted file mode 100644 index d5c7852285..0000000000 --- a/src/Integration.Vsix/CFamily/CFamilyIssueToAnalysisIssueConverter.cs +++ /dev/null @@ -1,326 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Diagnostics; -using System.IO; -using System.IO.Abstractions; -using System.Linq; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Utilities; -using SonarLint.VisualStudio.CFamily.Analysis; -using SonarLint.VisualStudio.CFamily.Rules; -using SonarLint.VisualStudio.CFamily.SubProcess; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Analysis; -using SonarLint.VisualStudio.Core.Configuration; -using SonarLint.VisualStudio.Core.ETW; -using SonarLint.VisualStudio.Core.UserRuleSettings; -using SonarLint.VisualStudio.Infrastructure.VS.Editor; - -/* Instancing: a new issue converter should be created for each analysis run. - * - * Overview - * -------- - * Each analysis is for a single file, although the issues returned could be for related - * files (e.g. a header file). The results will generally be a small set of files - the - * analysis file + [zero or more related files]. - * - * Converting an issue entails loading the text document for the file. We don't want to load - * the same document multiple times, so we'll create a separate converter for each analysis, - * so the converter can cache the loaded text documents. - */ - -namespace SonarLint.VisualStudio.Integration.Vsix.CFamily -{ - [Export(typeof(ICFamilyIssueConverterFactory))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal class CFamilyIssueConverterFactory : ICFamilyIssueConverterFactory - { - private readonly ITextDocumentFactoryService textDocumentFactoryService; - private readonly IContentTypeRegistryService contentTypeRegistryService; - private readonly IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration; - - [ImportingConstructor] - public CFamilyIssueConverterFactory(ITextDocumentFactoryService textDocumentFactoryService, IContentTypeRegistryService contentTypeRegistryService, IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration) - { - this.textDocumentFactoryService = textDocumentFactoryService; - this.contentTypeRegistryService = contentTypeRegistryService; - this.connectedModeFeaturesConfiguration = connectedModeFeaturesConfiguration; - } - - public ICFamilyIssueToAnalysisIssueConverter Create() - { - return new CFamilyIssueToAnalysisIssueConverter(textDocumentFactoryService, contentTypeRegistryService, connectedModeFeaturesConfiguration); - } - } - - // Short-lived class - one instance per analysis - internal class CFamilyIssueToAnalysisIssueConverter : ICFamilyIssueToAnalysisIssueConverter - { - private readonly ITextDocumentFactoryService textDocumentFactoryService; - private readonly ILineHashCalculator lineHashCalculator; - private readonly IFileSystem fileSystem; - private readonly IContentType filesContentType; - private readonly Dictionary pathToTextDocMap; - private readonly IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration; - - public CFamilyIssueToAnalysisIssueConverter(ITextDocumentFactoryService textDocumentFactoryService, IContentTypeRegistryService contentTypeRegistryService, IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration) - : this(textDocumentFactoryService, contentTypeRegistryService, connectedModeFeaturesConfiguration, new LineHashCalculator(), new FileSystem()) - { - } - - internal CFamilyIssueToAnalysisIssueConverter(ITextDocumentFactoryService textDocumentFactoryService, - IContentTypeRegistryService contentTypeRegistryService, - IConnectedModeFeaturesConfiguration connectedModeFeaturesConfiguration, - ILineHashCalculator lineHashCalculator, - IFileSystem fileSystem) - { - this.textDocumentFactoryService = textDocumentFactoryService; - this.lineHashCalculator = lineHashCalculator; - this.fileSystem = fileSystem; - this.connectedModeFeaturesConfiguration = connectedModeFeaturesConfiguration; - - filesContentType = contentTypeRegistryService.UnknownContentType; - - pathToTextDocMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - public IAnalysisIssue Convert(Message cFamilyIssue, string sqLanguage, ICFamilyRulesConfig rulesConfiguration) - { - CodeMarkers.Instance.CFamilyConvertIssueStart(cFamilyIssue.Filename); - - // Lines and character positions are 1-based - Debug.Assert(cFamilyIssue.Line > 0); - - // BUT special case of EndLine=0, Column=0, EndColumn=0 meaning "select the whole line" - Debug.Assert(cFamilyIssue.EndLine >= 0); - Debug.Assert(cFamilyIssue.Column > 0 || cFamilyIssue.Column == 0); - Debug.Assert(cFamilyIssue.EndColumn > 0 || cFamilyIssue.EndLine == 0); - - var ruleMetaData = rulesConfiguration.RulesMetadata[cFamilyIssue.RuleKey]; - - // Look up default severity and type - var defaultSeverity = ruleMetaData.DefaultSeverity; - var defaultType = ruleMetaData.Type; - Impact highestImpact = null; - - if (ruleMetaData.Type != IssueType.SecurityHotspot && connectedModeFeaturesConfiguration.IsNewCctAvailable()) - { - highestImpact = GetHighestImpact(ruleMetaData); - } - - var fileContents = GetFileContentsOfReportedFiles(cFamilyIssue); - - var locations = cFamilyIssue.Parts - .Select(x => ToAnalysisIssueLocation(x, fileContents)) - .ToArray(); - - // If PartsMakeFlow is set to true the issues are expected to be in the reversed order - if (cFamilyIssue.PartsMakeFlow) - { - Array.Reverse(locations); - } - - var flows = locations.Any() ? new[] { new AnalysisIssueFlow(locations) } : null; - - var result = ToAnalysisIssue(cFamilyIssue, sqLanguage, defaultSeverity, defaultType, flows, fileContents, highestImpact); - - CodeMarkers.Instance.CFamilyConvertIssueStop(); - - return result; - } - - private IReadOnlyDictionary GetFileContentsOfReportedFiles(Message cFamilyIssue) - { - var filePaths = cFamilyIssue.Parts - .Select(x => x.Filename) - .Union(new[] { cFamilyIssue.Filename }) - .Distinct(); - - foreach (var filePath in filePaths) - { - if (pathToTextDocMap.ContainsKey(filePath)) - { - CodeMarkers.Instance.CFamilyConvertIssueFileAlreadyLoaded(filePath); - } - else - { - var doc = GetTextDocument(filePath); - pathToTextDocMap.Add(filePath, doc); - - CodeMarkers.Instance.CFamilyConvertIssueFileLoaded(filePath); - } - } - - return pathToTextDocMap; - } - - private ITextDocument GetTextDocument(string filePath) - { - if (fileSystem.File.Exists(filePath)) - { - // The document is being loaded from disc, so it should match the version that was analyzed by the subprocess - var doc = textDocumentFactoryService.CreateAndLoadTextDocument(filePath, filesContentType); - return doc; - } - - return null; - } - - private IAnalysisIssue ToAnalysisIssue(Message cFamilyIssue, - string sqLanguage, - IssueSeverity defaultSeverity, - IssueType defaultType, - IReadOnlyList flows, - IReadOnlyDictionary fileContents, - Impact highestImpact) - { - return new AnalysisIssue - ( - id: null, // until CFamily is migrated to SlCore, its ID will be null - ruleKey: sqLanguage + ":" + cFamilyIssue.RuleKey, - severity: Convert(defaultSeverity), - type: Convert(defaultType), - highestImpact, - primaryLocation: ToAnalysisIssueLocation(cFamilyIssue, fileContents), - flows: flows, - fixes: ToQuickFixes(cFamilyIssue) - ); - } - - private static List ToQuickFixes(Message cFamilyIssue) - { - return cFamilyIssue.Fixes.Select(f => - new QuickFix( - message: f.Message, - f.Edits?.Select(e => - new Core.Analysis.Edit( - textRange: new TextRange( - startLine: e.StartLine, - startLineOffset: e.StartColumn - 1, - endLine: e.EndLine, - endLineOffset: e.EndColumn - 1, - lineHash: null), - text: e.Text)) - .ToList())) - .ToList(); - } - - private string CalculateLineHash(MessagePart cFamilyIssueLocation, IReadOnlyDictionary fileContents) - { - var isFileLevelLocation = cFamilyIssueLocation.Line == 1 && - cFamilyIssueLocation.Column <= 1 && - cFamilyIssueLocation.EndColumn == 0 && - cFamilyIssueLocation.EndLine == 0; - - if (isFileLevelLocation) - { - return null; - } - - var textSnapshot = fileContents[cFamilyIssueLocation.Filename]?.TextBuffer?.CurrentSnapshot; - - return textSnapshot == null ? null : lineHashCalculator.Calculate(textSnapshot, cFamilyIssueLocation.Line); - } - - private AnalysisIssueLocation ToAnalysisIssueLocation(MessagePart cFamilyIssueLocation, - IReadOnlyDictionary fileContents) => - new AnalysisIssueLocation - ( - filePath: Path.IsPathRooted(cFamilyIssueLocation.Filename) - ? Path.GetFullPath(cFamilyIssueLocation.Filename) - : cFamilyIssueLocation.Filename, - message: cFamilyIssueLocation.Text, - textRange: new TextRange( - lineHash: CalculateLineHash(cFamilyIssueLocation, fileContents), - startLine: cFamilyIssueLocation.Line, - endLine: cFamilyIssueLocation.EndLine, - - // We don't care about the columns in the special case EndLine=0 - startLineOffset: cFamilyIssueLocation.EndLine == 0 ? 0 : cFamilyIssueLocation.Column - 1, - endLineOffset: cFamilyIssueLocation.EndLine == 0 ? 0 : cFamilyIssueLocation.EndColumn - 1 - )); - - /// - /// Converts from the CFamily issue severity enum to the standard AnalysisIssueSeverity - /// - internal /* for testing */ static AnalysisIssueSeverity Convert(IssueSeverity issueSeverity) - { - switch (issueSeverity) - { - case IssueSeverity.Blocker: - return AnalysisIssueSeverity.Blocker; - - case IssueSeverity.Critical: - return AnalysisIssueSeverity.Critical; - - case IssueSeverity.Info: - return AnalysisIssueSeverity.Info; - - case IssueSeverity.Major: - return AnalysisIssueSeverity.Major; - - case IssueSeverity.Minor: - return AnalysisIssueSeverity.Minor; - - default: - throw new ArgumentOutOfRangeException(nameof(issueSeverity)); - } - } - - /// - /// Converts from the CFamily issue type enum to the standard AnalysisIssueType - /// - internal /* for testing */static AnalysisIssueType Convert(IssueType issueType) - { - switch (issueType) - { - case IssueType.Bug: - return AnalysisIssueType.Bug; - - case IssueType.CodeSmell: - return AnalysisIssueType.CodeSmell; - - case IssueType.Vulnerability: - return AnalysisIssueType.Vulnerability; - - case IssueType.SecurityHotspot: - return AnalysisIssueType.SecurityHotspot; - - default: - throw new ArgumentOutOfRangeException(nameof(issueType)); - } - } - - internal /* for testing */ static Impact GetHighestImpact(RuleMetadata ruleMetadata) - { - if (ruleMetadata?.Code?.Impacts == null || ruleMetadata.Code.Impacts.Count == 0) - { - return null; - } - - var highestImpact = ruleMetadata.Code.Impacts.OrderByDescending(kvp => kvp.Value).ThenByDescending(kvp => kvp.Key).First(); - return new Impact(highestImpact.Key, highestImpact.Value); - } - } -}