Skip to content

Commit

Permalink
SLVS-1717 Fix C++ Language standard recognition when a C language sta…
Browse files Browse the repository at this point in the history
…ndard is also specified

* Only set one language standard for the file
* Always set either /TC or /TP flag for the file (/TC for C and /TP for CPP)
* Assume the file is C if CompileAs == CompileAsC or if CompileAs != CompileAsCpp and vcFile.ContentType == CCode
* ASsume the file is CPP otherwise
  • Loading branch information
georgii-borovinskikh-sonarsource committed Jan 10, 2025
1 parent 3a63a13 commit e144488
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 164 deletions.
120 changes: 22 additions & 98 deletions src/Integration.Vsix.UnitTests/CFamily/CmdBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.VCProjectEngine;
using Moq;
using SonarLint.VisualStudio.Integration.Vsix.CFamily.VcxProject;


Expand All @@ -31,27 +27,6 @@ namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily
[TestClass]
public class CmdBuilderTests
{

[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
[DataRow("CompileAsC", "/TC")]
[DataRow("CompileAsCpp", "/TP")]
[DataRow("Invalid", "Unsupported CompileAs: Invalid")]
public void ConvertCompileAsAndGetLanguage(string input, string output)
{
if ("Invalid".Equals(input))
{
Action action = () => CmdBuilder.ConvertCompileAsAndGetLanguage(input);
action.Should().ThrowExactly<ArgumentException>().And.Message.Should()
.StartWith(output);
}
else
{
CmdBuilder.ConvertCompileAsAndGetLanguage(input).Should().Be(output);
}
}

[TestMethod]
[DataRow("", "")]
[DataRow("false", "")]
Expand Down Expand Up @@ -162,52 +137,6 @@ public void ConvertBasicRuntimeChecks(string input, string output)
}
}

[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
[DataRow(null, "")]
[DataRow("stdcpplatest", "/std:c++latest")]
[DataRow("stdcpp20", "/std:c++20")]
[DataRow("stdcpp17", "/std:c++17")]
[DataRow("stdcpp14", "/std:c++14")]
[DataRow("Invalid", "Unsupported LanguageStandard: Invalid")]
public void ConvertCppStandard(string input, string output)
{
if ("Invalid".Equals(input))
{
Action action = () => CmdBuilder.ConvertCppStandard(input);
action.Should().ThrowExactly<ArgumentException>().And.Message.Should()
.StartWith(output);
}
else
{
CmdBuilder.ConvertCppStandard(input).Should().Be(output);
}
}

[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
[DataRow(null, "")]
[DataRow("stdclatest", "/std:clatest")]
[DataRow("stdc23", "/std:c23")]
[DataRow("stdc17", "/std:c17")]
[DataRow("stdc11", "/std:c11")]
[DataRow("Invalid", "Unsupported LanguageStandard_C: Invalid")]
public void ConvertCStandard(string input, string output)
{
if ("Invalid".Equals(input))
{
Action action = () => CmdBuilder.ConvertCStandard(input);
action.Should().ThrowExactly<ArgumentException>().And.Message.Should()
.StartWith(output);
}
else
{
CmdBuilder.ConvertCStandard(input).Should().Be(output);
}
}

[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
Expand Down Expand Up @@ -255,16 +184,6 @@ public void ConvertStructMemberAlignment(string input, string output)
[DataRow("UseStandardPreprocessor", "false", "/Zc:preprocessor- ")]
[DataRow("ConformanceMode", "true", "/permissive- ")]
[DataRow("ConformanceMode", "false", "/permissive ")]
[DataRow("LanguageStandard", "stdcpplatest", "/std:c++latest ")]
[DataRow("LanguageStandard", "stdcpp20", "/std:c++20 ")]
[DataRow("LanguageStandard", "stdcpp17", "/std:c++17 ")]
[DataRow("LanguageStandard", "stdcpp14", "/std:c++14 ")]
[DataRow("LanguageStandard", "Default", "")]
[DataRow("LanguageStandard_C", "stdclatest", "/std:clatest ")]
[DataRow("LanguageStandard_C", "stdc23", "/std:c23 ")]
[DataRow("LanguageStandard_C", "stdc17", "/std:c17 ")]
[DataRow("LanguageStandard_C", "stdc11", "/std:c11 ")]
[DataRow("LanguageStandard_C", "Default", "")]
[DataRow("ExceptionHandling", "Sync", "/EHsc ")]
[DataRow("ExceptionHandling", "SyncCThrow", "/EHs ")]
[DataRow("ExceptionHandling", "Async", "/EHa ")]
Expand Down Expand Up @@ -294,9 +213,6 @@ public void ConvertStructMemberAlignment(string input, string output)
[DataRow("StructMemberAlignment", "4Bytes", "/Zp4 ")]
[DataRow("StructMemberAlignment", "2Bytes", "/Zp2 ")]
[DataRow("StructMemberAlignment", "1Bytes", "/Zp1 ")]
[DataRow("CompileAs", "Default", "")]
[DataRow("CompileAs", "CompileAsC", "/TC ")]
[DataRow("CompileAs", "CompileAsCpp", "/TP ")]
[DataRow("AdditionalIncludeDirectories", "a;;b;c", "/I\"a\" /I\"b\" /I\"c\" ")]
[DataRow("AdditionalIncludeDirectories", ";;;", "")]
[DataRow("PreprocessorDefinitions", "a;;b;;c;", "/D\"a\" /D\"b\" /D\"c\" ")]
Expand All @@ -307,7 +223,7 @@ public void ConvertStructMemberAlignment(string input, string output)
[DataRow("AdditionalOptions", "/MD /Zc:wchar_t-", "/MD /Zc:wchar_t- ")]
public void AddOptFromProperties(string input, string output, string cmd)
{
var cmdBuilder = new CmdBuilder(false);
var cmdBuilder = new CmdBuilder(false, Substitute.For<ILanguageFlagsProvider>());
var settingsMock = new Mock<IVCRulePropertyStorage>();
settingsMock.Setup(x => x.GetEvaluatedPropertyValue(It.IsAny<string>())).Returns<string>(s => s == input ? output : "");

Expand All @@ -316,21 +232,29 @@ public void AddOptFromProperties(string input, string output, string cmd)
cmdBuilder.GetFullCmd().Should().Be(cmd);
}

[TestMethod]
[DataRow("Default", "")]
[DataRow("CompileAsC", "/TC ")]
[DataRow("CompileAsCpp", "/TP ")]
public void HeaderFileLang(string compileAs, string cmd)
[DataTestMethod]
[DataRow(false, "", "", "")]
[DataRow(true, "", "", "")]
[DataRow(false, "1", "", "1 ")]
[DataRow(true, "1", "", "1 ")]
[DataRow(false, "", "2", "2 ")]
[DataRow(true, "", "2", "2 ")]
[DataRow(false, "1", "2", "1 2 ")]
[DataRow(true, "1", "2", "1 2 ")]
public void AddOptFromProperties_DelegatesToLanguageFlagsProvider_AddsAvailableLanguageFlags(bool isHeader, string compileAsFlag, string languageStandardFlag, string expectedCmd)
{
var cmdBuilder = new CmdBuilder(true);
var settingsMock = new Mock<IVCRulePropertyStorage>();
settingsMock.Setup(x => x.GetEvaluatedPropertyValue(It.IsAny<string>())).Returns<string>(s => s == "CompileAs" ? compileAs : "");
var languageFlagsProvider = Substitute.For<ILanguageFlagsProvider>();
var vcRulePropertyStorage = Substitute.For<IVCRulePropertyStorage>();
vcRulePropertyStorage.GetEvaluatedPropertyValue("CompileAs").Returns("CompileAsValue");
vcRulePropertyStorage.GetEvaluatedPropertyValue("LanguageStandard_C").Returns("LanguageStandardCValue");
vcRulePropertyStorage.GetEvaluatedPropertyValue("LanguageStandard").Returns("LanguageStandardValue");
languageFlagsProvider.GetLanguageConfiguration("CompileAsValue", "LanguageStandardCValue", "LanguageStandardValue").Returns((compileAsFlag, languageStandardFlag));
var cmdBuilder = new CmdBuilder(isHeader, languageFlagsProvider);

cmdBuilder.AddOptFromProperties(settingsMock.Object);
cmdBuilder.AddOptFromProperties(vcRulePropertyStorage);

cmdBuilder.GetFullCmd().Should().Be(cmd);
cmdBuilder.GetFullCmd().Should().Be(expectedCmd);
}

[TestMethod]
[DataRow("Create", false, "", "/Yc\"C:\\pch.h\" ")]
[DataRow("Create", true, "", "/Yc\"C:\\pch.h\" ")]
Expand All @@ -340,7 +264,7 @@ public void HeaderFileLang(string compileAs, string cmd)
[DataRow("Use", true, "C:\\a.h", "/FI\"C:\\a.h\" /Yu\"C:\\pch.h\" ")]
public void PCHCreate(string mode, bool isHeader, string forceInclude, string command)
{
var cmdBuilder = new CmdBuilder(isHeader);
var cmdBuilder = new CmdBuilder(isHeader, Substitute.For<ILanguageFlagsProvider>());
var settingsMock = new Mock<IVCRulePropertyStorage>();
settingsMock.Setup(x => x.GetEvaluatedPropertyValue(It.IsAny<string>())).Returns<string>(s =>
{
Expand Down Expand Up @@ -369,7 +293,7 @@ public void PCHCreate(string mode, bool isHeader, string forceInclude, string co
[TestMethod]
public void PCHUse()
{
var cmdBuilder = new CmdBuilder(false);
var cmdBuilder = new CmdBuilder(false, Substitute.For<ILanguageFlagsProvider>());
var settingsMock = new Mock<IVCRulePropertyStorage>();
settingsMock.Setup(x => x.GetEvaluatedPropertyValue(It.IsAny<string>())).Returns<string>(s =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2025 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.Integration.Vsix.CFamily.VcxProject;

namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily.VcxProject;

[TestClass]
public class CompileAsValueConverterTests
{
[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
[DataRow("CompileAsC", "/TC")]
[DataRow("CompileAsCpp", "/TP")]
public void GetFlagValue_ConvertsToCorrectFlagValue(string input, string output) =>
CompileAsValueConverter.GetFlagValue(input).Should().Be(output);

[TestMethod]
public void GetFlagValue_UnsupportedPropertyValue_Throws()
{
var act = () => CompileAsValueConverter.GetFlagValue("INVALID");

act.Should().Throw<ArgumentException>().WithMessage("Unsupported CompileAs: INVALID");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2025 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.Integration.Vsix.CFamily.VcxProject;

namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily.VcxProject;

[TestClass]
public class LanguageFlagsProviderTests
{
[TestMethod]
public void GetLanguageConfiguration_DefaultCompileAs_NoContentType_ReturnsCpp() =>
new LanguageFlagsProvider("unknown")
.GetLanguageConfiguration("Default", "stdc23", "stdcpp20")
.Should().Be(("/TP", "/std:c++20"));

[TestMethod]
public void GetLanguageConfiguration_DefaultCompileAs_ContentTypeCppCode_ReturnsCpp() =>
new LanguageFlagsProvider("CppCode")
.GetLanguageConfiguration("Default", "stdc23", "stdcpp20")
.Should().Be(("/TP", "/std:c++20"));

[TestMethod]
public void GetLanguageConfiguration_DefaultCompileAs_ContentTypeCppHeader_ReturnsCpp() =>
new LanguageFlagsProvider("CppHeader")
.GetLanguageConfiguration("Default", "stdc23", "stdcpp20")
.Should().Be(("/TP", "/std:c++20"));

[TestMethod]
public void GetLanguageConfiguration_DefaultCompileAs_ContentTypeCCode_ReturnsC() =>
new LanguageFlagsProvider("CCode")
.GetLanguageConfiguration("Default", "stdc23", "stdcpp20")
.Should().Be(("/TC", "/std:c23"));

[DataTestMethod]
[DataRow("CppCode")]
[DataRow("CCode")]
[DataRow("Default")]
public void GetLanguageConfiguration_CompileAsC_AnyContentType_ReturnsC(string contentType) =>
new LanguageFlagsProvider(contentType)
.GetLanguageConfiguration("CompileAsC", "stdc23", "stdcpp20")
.Should().Be(("/TC", "/std:c23"));

[DataTestMethod]
[DataRow("CppCode")]
[DataRow("CCode")]
[DataRow("Default")]
public void GetLanguageConfiguration_CompileAsCpp_AnyContentType_ReturnsCpp(string contentType) =>
new LanguageFlagsProvider(contentType)
.GetLanguageConfiguration("CompileAsCpp", "stdc23", "stdcpp20")
.Should().Be(("/TP", "/std:c++20"));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2025 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.Integration.Vsix.CFamily.VcxProject;

namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily.VcxProject;

[TestClass]
public class LanguageStandardConverterTests
{

[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
[DataRow(null, "")]
[DataRow("stdcpplatest", "/std:c++latest")]
[DataRow("stdcpp20", "/std:c++20")]
[DataRow("stdcpp17", "/std:c++17")]
[DataRow("stdcpp14", "/std:c++14")]
public void GetCppStandardFlagValue(string input, string output) =>
LanguageStandardConverter.GetCppStandardFlagValue(input).Should().Be(output);

[TestMethod]
public void GetCppStandardFlagValue_UnsupportedValue_Throws()
{
var act = () => LanguageStandardConverter.GetCppStandardFlagValue("INVALID");

act.Should().Throw<ArgumentException>().WithMessage("Unsupported LanguageStandard: INVALID");
}

[TestMethod]
[DataRow("", "")]
[DataRow("Default", "")]
[DataRow(null, "")]
[DataRow("stdclatest", "/std:clatest")]
[DataRow("stdc23", "/std:c23")]
[DataRow("stdc17", "/std:c17")]
[DataRow("stdc11", "/std:c11")]
public void GetCStandardFlagValue(string input, string output) =>
LanguageStandardConverter.GetCStandardFlagValue(input).Should().Be(output);

[TestMethod]
public void GetCStandardFlagValue_UnsupportedValue_Throws()
{
var act = () => LanguageStandardConverter.GetCStandardFlagValue("INVALID");

act.Should().Throw<ArgumentException>().WithMessage("Unsupported LanguageStandard_C: INVALID");
}
}
Loading

0 comments on commit e144488

Please sign in to comment.