Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLVS-1717 Fix C++ Language standard recognition when a C language standard is also specified #5948

Merged

Conversation

georgii-borovinskikh-sonarsource
Copy link
Contributor

@georgii-borovinskikh-sonarsource georgii-borovinskikh-sonarsource commented Jan 10, 2025

  • 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 CompileAsC or if not CompileAsCpp and vcFile.ContentType is CCode
  • Assume the file is CPP otherwise

Note: it's entirely possible to have a file with CompileAsCpp and CCode, CompileAsC and CppCode, Default and CppHeader or any other combination of these values by manually setting the overrides in vcxproj. For consistency, CompileAs is chosen as the primary indicator for the language, vcFile.ContentType is only used if CompileAs is not set to CompileAsCpp

…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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I only have some minor feedback.

var projectItemConfig = new ProjectItemConfig
{
ItemType = "ClCompile",
FileConfigProperties = new Dictionary<string, string>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract the creation of the properties in a method to prevent cluttering the test. Then reuse the method in all other tests.

private void AddLanguageFlags(IVCRulePropertyStorage properties)
{
var (compileAsFlag, languageStandardFlag) = languageFlagsProvider.GetLanguageConfiguration(
GetPotentiallyUnsupportedPropertyValue(properties, "CompileAs", ""),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use string.Empty

Suggested change
GetPotentiallyUnsupportedPropertyValue(properties, "CompileAs", ""),
GetPotentiallyUnsupportedPropertyValue(properties, "CompileAs", string.Empty),

{
var (compileAsFlag, languageStandardFlag) = languageFlagsProvider.GetLanguageConfiguration(
GetPotentiallyUnsupportedPropertyValue(properties, "CompileAs", ""),
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard_C", ""),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard_C", ""),
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard_C", string.Empty),

var (compileAsFlag, languageStandardFlag) = languageFlagsProvider.GetLanguageConfiguration(
GetPotentiallyUnsupportedPropertyValue(properties, "CompileAs", ""),
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard_C", ""),
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard", ""));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard", ""));
GetPotentiallyUnsupportedPropertyValue(properties, "LanguageStandard", string.Empty));

value switch
{
// https://github.com/SonarSource/sonarlint-visualstudio/issues/738
"" or "Default" => "",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"" or "Default" => "",
"" or "Default" => string.Empty,

public static string GetCppStandardFlagValue(string value) =>
value switch
{
null or "" or "Default" => "",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
null or "" or "Default" => "",
null or "" or "Default" => string.Empty,

public static string GetCStandardFlagValue(string value) =>
value switch
{
null or "" or "Default" => "",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
null or "" or "Default" => "",
null or "" or "Default" => string.Empty,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for taking care of this 🙏

It seems to work as expected. I left two minor comments, and I am letting you decide if they are worth addressing!

{
var compileAsFlag = CompileAsValueConverter.GetFlagValue(compileAs);

return compileAsFlag == CompileAsCFlag || (compileAsFlag != CompileAsCppFlag && vcFileContentType == ContentTypeCCode)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to add a comment to explain why checking compileAsFlag isn't sufficient?

I think we mentioned that we now need vcFileContentType as well for .c files when compileAsFlag is kept as default.

[DataRow(true, "", "", "")]
[DataRow(false, "1", "", "1 ")]
[DataRow(true, "1", "", "1 ")]
[DataRow(false, "", "2", "2 ")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comment:

The real LanguageFlagsProvider seems to always return a non-empty string for the compile-as flag (i.e. either /TC or /TP, but never blank), as described in the PR summary. This implies that the expected command should always have either of these.

I was confused by the expected "2 " here (which means that no /TC or /TP were added). As far as I understand, the mock in this test doesn't behave similarly to the real implementation since it returns empty compileAs switches.

Would it make sense to somehow align the behavior of the mock here with the real implementation by always returning a non-empty for compileAsFlag?

(I would leave it for you to decide if it is worth it, I am just sharing it because it took me a bit to understand what is going on)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of the test was more to show that the implementation for choosing the values is delegated and should be tested in the LanguageFlagsProviderTests. I will just change the values to be more readable than just 1 & 2.

@georgii-borovinskikh-sonarsource georgii-borovinskikh-sonarsource merged commit b829dc5 into master Jan 21, 2025
3 checks passed
@georgii-borovinskikh-sonarsource georgii-borovinskikh-sonarsource deleted the gb/fix-cfamily-language-standard branch January 21, 2025 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants