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

Add Clang-Tidy converter #2367

Merged
merged 3 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ReleaseHistory.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# SARIF Package Release History (SDK, Driver, Converters, and Multitool)

* FEATURE: Add Clang-Tidy converter. [#2367](https://github.com/microsoft/sarif-sdk/pull/2367)

## **v2.4.9** [Sdk](https://www.nuget.org/packages/Sarif.Sdk/2.4.9) | [Driver](https://www.nuget.org/packages/Sarif.Driver/2.4.9) | [Converters](https://www.nuget.org/packages/Sarif.Converters/2.4.9) | [Multitool](https://www.nuget.org/packages/Sarif.Multitool/2.4.9) | [Multitool Library](https://www.nuget.org/packages/Sarif.Multitool.Library/2.4.9)
* FEATURE: Report inner exception details if available. [#2357](https://github.com/microsoft/sarif-sdk/pull/2357)
* FEATURE: Add support for git blame. [#2358](https://github.com/microsoft/sarif-sdk/pull/2358)
Expand Down
1 change: 1 addition & 0 deletions src/Sarif.Converters/BuiltInConverterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public override ToolFileConverterBase CreateConverterCore(string toolFormat)
CreateConverterRecord<AndroidStudioConverter>(result, ToolFormat.AndroidStudio);
CreateConverterRecord<CppCheckConverter>(result, ToolFormat.CppCheck);
CreateConverterRecord<ClangAnalyzerConverter>(result, ToolFormat.ClangAnalyzer);
CreateConverterRecord<ClangTidyConverter>(result, ToolFormat.ClangTidy);
CreateConverterRecord<ContrastSecurityConverter>(result, ToolFormat.ContrastSecurity);
CreateConverterRecord<FortifyConverter>(result, ToolFormat.Fortify);
CreateConverterRecord<FortifyFprConverter>(result, ToolFormat.FortifyFpr);
Expand Down
165 changes: 165 additions & 0 deletions src/Sarif.Converters/ClangTidyConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Microsoft.CodeAnalysis.Sarif.Converters.ClangTidyObjectModel;

using YamlDotNet.Serialization;

namespace Microsoft.CodeAnalysis.Sarif.Converters
{
public class ClangTidyConverter : ToolFileConverterBase
{
private const string ToolInformationUri = "https://clang.llvm.org/extra/clang-tidy/";

public override string ToolName => "Clang-Tidy";

public override void Convert(Stream input, IResultLogWriter output, OptionallyEmittedData dataToInsert)
{
input = input ?? throw new ArgumentNullException(nameof(input));
output = output ?? throw new ArgumentNullException(nameof(output));

IDeserializer deserializer = new DeserializerBuilder().Build();
using var textReader = new StreamReader(input);
ClangTidyLog log = deserializer.Deserialize<ClangTidyLog>(textReader);

(List<ReportingDescriptor>, List<Result>) rulesAndResults = ExtractRulesAndResults(log);

var run = new Run
{
Tool = new Tool
{
Driver = new ToolComponent
{
Name = ToolName,
InformationUri = new Uri(ToolInformationUri),
Rules = rulesAndResults.Item1,
}
},
Results = rulesAndResults.Item2,
};

PersistResults(output, rulesAndResults.Item2, run);
}

internal static Result CreateResult(ClangTidyDiagnostic entry)
{
entry = entry ?? throw new ArgumentNullException(nameof(entry));

Result result = new Result()
{
RuleId = entry.DiagnosticName,
Copy link
Collaborator

@eddynaka eddynaka Jun 15, 2021

Choose a reason for hiding this comment

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

entry.DiagnosticName

from this, you can create the ReportingDescriptor #Closed

Copy link
Collaborator

Choose a reason for hiding this comment

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

each rule can point to its own documentation: https://clang.llvm.org/extra/clang-tidy/checks/modernize-use-trailing-return-type.html

change modernize-use-trailing-return-type to another ruleid and it will work

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added the rules.

Message = new Message { Text = entry.DiagnosticMessage.Message },
Kind = ResultKind.Fail
};

// no level infomation in Clang-Tidy report
result.Level = FailureLevel.Warning;

Region region = new Region()
{
CharOffset = entry.DiagnosticMessage.FileOffset,
};

Uri analysisTargetUri = new Uri(entry.DiagnosticMessage.FilePath, UriKind.RelativeOrAbsolute);

var physicalLocation = new PhysicalLocation
{
ArtifactLocation = new ArtifactLocation
{
Uri = analysisTargetUri
},
Region = region
};

Location location = new Location()
{
PhysicalLocation = physicalLocation
};

result.Locations = new List<Location>()
{
location
};

if (entry.DiagnosticMessage.Replacements.Count > 0)
{
IList<Replacement> replacements = new List<Replacement>();

foreach (ClangTidyReplacement fix in entry.DiagnosticMessage.Replacements)
{
Replacement replacement = new Replacement();

replacement.DeletedRegion = new Region
{
CharLength = fix.Length,
CharOffset = fix.Offset,
};

if (!string.IsNullOrEmpty(fix.ReplacementText))
{
replacement.InsertedContent = new ArtifactContent
{
Text = fix.ReplacementText
};
}

replacements.Add(replacement);
}

var sarifFileChange = new ArtifactChange
{
ArtifactLocation = new ArtifactLocation
{
Uri = analysisTargetUri
},
Replacements = replacements
};

Fix sarifFix = new Fix(description: null, artifactChanges: new List<ArtifactChange>() { sarifFileChange }, properties: null);
result.Fixes = new List<Fix> { sarifFix };
}

return result;
}

private static (List<ReportingDescriptor>, List<Result>) ExtractRulesAndResults(ClangTidyLog log)
{
var rules = new Dictionary<string, ReportingDescriptor>(StringComparer.OrdinalIgnoreCase);
var results = new List<Result>();

foreach (ClangTidyDiagnostic diagnostic in log.Diagnostics)
{
string ruleId = diagnostic.DiagnosticName;
(ReportingDescriptor, Result) ruleAndResult = SarifRuleAndResultFromClangTidyDiagnostic(diagnostic);
if (!rules.ContainsKey(ruleId))
{
rules.Add(ruleId, ruleAndResult.Item1);
}

results.Add(ruleAndResult.Item2);
}

return (rules.Values.ToList(), results);
}

private static (ReportingDescriptor, Result) SarifRuleAndResultFromClangTidyDiagnostic(ClangTidyDiagnostic diagnostic)
{
var reportingDescriptor = new ReportingDescriptor
{
Id = diagnostic.DiagnosticName,
HelpUri = diagnostic.DiagnosticName.Equals("clang-diagnostic-error", StringComparison.OrdinalIgnoreCase)
? null
: new Uri($"https://clang.llvm.org/extra/clang-tidy/checks/{diagnostic.DiagnosticName}.html")
};

Result result = CreateResult(diagnostic);

return (reportingDescriptor, result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.CodeAnalysis.Sarif.Converters.ClangTidyObjectModel
{
public class ClangTidyDiagnostic
{
public string DiagnosticName { get; set; }
public ClangTidyDiagnosticMessage DiagnosticMessage { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;

namespace Microsoft.CodeAnalysis.Sarif.Converters.ClangTidyObjectModel
{
public class ClangTidyDiagnosticMessage
{
public string Message { get; set; }
public string FilePath { get; set; }
public int FileOffset { get; set; }
public List<ClangTidyReplacement> Replacements { get; set; }
}
}
13 changes: 13 additions & 0 deletions src/Sarif.Converters/ClangTidyObjectModel/ClangTidyLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;

namespace Microsoft.CodeAnalysis.Sarif.Converters.ClangTidyObjectModel
{
public class ClangTidyLog
{
public string MainSourceFile { get; set; }
public List<ClangTidyDiagnostic> Diagnostics { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.CodeAnalysis.Sarif.Converters.ClangTidyObjectModel
{
public class ClangTidyReplacement
{
public string FilePath { get; set; }
public int Offset { get; set; }
public int Length { get; set; }
public string ReplacementText { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/Sarif.Converters/Sarif.Converters.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
<PackageReference Include="CsvHelper" Version="15.0.5" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="YamlDotNet" Version="11.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/Sarif.Converters/ToolFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public static class ToolFormat
/// <summary>Clang analyzer's file format.</summary>
public const string ClangAnalyzer = nameof(ClangAnalyzer);

/// <summary>Clang analyzer's file format.</summary>
public const string ClangTidy = nameof(ClangTidy);

/// <summary>Contrast Security's file format.</summary>
public const string ContrastSecurity = nameof(ContrastSecurity);

Expand Down
2 changes: 1 addition & 1 deletion src/Sarif.Multitool.Library/ConvertOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class ConvertOptions : SingleFileOptionsBase
[Option(
't',
"tool",
HelpText = "The tool format of the input file. Must be one of: AndroidStudio, ClangAnalyzer, CppCheck, ContrastSecurity, FlawFinder, Fortify, FortifyFpr, FxCop, Hdf, PREfast, Pylint, SemmleQL, StaticDriverVerifier, TSLint, or a tool format for which a plugin assembly provides the converter.",
HelpText = "The tool format of the input file. Must be one of: AndroidStudio, ClangAnalyzer, ClangTidy, CppCheck, ContrastSecurity, FlawFinder, Fortify, FortifyFpr, FxCop, Hdf, PREfast, Pylint, SemmleQL, StaticDriverVerifier, TSLint, or a tool format for which a plugin assembly provides the converter.",
Required = true)]
public string ToolFormat { get; set; }

Expand Down
67 changes: 67 additions & 0 deletions src/Test.UnitTests.Sarif.Converters/ClangTidyConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.IO;

using FluentAssertions;

using Microsoft.CodeAnalysis.Sarif.Writers;

using Xunit;

using YamlDotNet.Core;

namespace Microsoft.CodeAnalysis.Sarif.Converters
{
public class ClangTidyConverterTests : ConverterTestsBase<ClangTidyConverter>
{
private static readonly string NoOutputExpected = string.Empty;

[Fact]
public void Converter_RequiresInputStream()
{
var converter = new ClangTidyConverter();
Action action = () => converter.Convert(input: null, output: new ResultLogObjectWriter(), dataToInsert: OptionallyEmittedData.None);
action.Should().Throw<ArgumentNullException>();
}

[Fact]
public void Converter_RequiresResultLogWriter()
{
var converter = new ClangTidyConverter();
Action action = () => converter.Convert(input: new MemoryStream(), output: null, dataToInsert: OptionallyEmittedData.None);
action.Should().Throw<ArgumentNullException>();
}

[Fact]
public void Converter_WhenInputIsEmpty_ReturnsNoResults()
{
string input = GetResourceText("Inputs.Empty.yaml");
string expectedOutput = GetResourceText("ExpectedOutputs.NoResults.sarif");
RunTestCase(input, expectedOutput);
}

[Fact]
public void Converter_WhenResultRowIsInvalid_ThrowsExpectedException()
{
string input = GetResourceText("Inputs.InvalidResult.yaml");
Action action = () => RunTestCase(input, NoOutputExpected);
action.Should().Throw<YamlException>();
}

[Fact]
public void Converter_WhenInputContainsValidResults_ReturnsExpectedOutput()
{
string input = GetResourceText("Inputs.ValidResults.yaml");
string expectedOutput = GetResourceText("ExpectedOutputs.ValidResults.sarif");
RunTestCase(input, expectedOutput);
}

private static readonly ResourceExtractor s_extractor = new ResourceExtractor(typeof(ClangTidyConverterTests));
private const string ResourceNamePrefix = ToolFormat.ClangTidy;

private static string GetResourceText(string resourceNameSuffix) =>
s_extractor.GetResourceText($"TestData.{ResourceNamePrefix}.{resourceNameSuffix}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
</PropertyGroup>

<ItemGroup>
<None Remove="TestData\ClangTidy\ExpectedOutputs\NoResults.sarif" />
<None Remove="TestData\ClangTidy\ExpectedOutputs\ValidResults.sarif" />
<None Remove="TestData\ClangTidy\Inputs\Empty.yaml" />
<None Remove="TestData\ClangTidy\Inputs\InvalidResult.yaml" />
<None Remove="TestData\ClangTidy\Inputs\ValidResults.yaml" />
<None Remove="TestData\Hdf\ExpectedOutputs\NoResults.sarif" />
<None Remove="TestData\Hdf\ExpectedOutputs\ValidResults.sarif" />
<None Remove="TestData\Hdf\Inputs\Empty.json" />
Expand Down Expand Up @@ -48,6 +53,11 @@
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="TestData\ClangTidy\ExpectedOutputs\NoResults.sarif" />
<EmbeddedResource Include="TestData\ClangTidy\ExpectedOutputs\ValidResults.sarif" />
<EmbeddedResource Include="TestData\ClangTidy\Inputs\Empty.yaml" />
<EmbeddedResource Include="TestData\ClangTidy\Inputs\InvalidResult.yaml" />
<EmbeddedResource Include="TestData\ClangTidy\Inputs\ValidResults.yaml" />
<EmbeddedResource Include="TestData\Hdf\ExpectedOutputs\NoResults.sarif" />
<EmbeddedResource Include="TestData\Hdf\ExpectedOutputs\ValidResults.sarif" />
<EmbeddedResource Include="TestData\Hdf\Inputs\ValidResults.json" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
"version": "2.1.0",
"runs": [
{
"results": [],
"tool": {
"driver": {
"name": "Clang-Tidy",
"informationUri": "https://clang.llvm.org/extra/clang-tidy/"
}
},
"columnKind": "utf16CodeUnits"
}
]
}
Loading