-
Notifications
You must be signed in to change notification settings - Fork 93
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
Emit column kind default explicitly for Windows SDK SARIF emit. #1160
Changes from 2 commits
c2ceb47
b5e1797
d0ea69d
3d50d89
83df6bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// 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 Xunit; | ||
using Newtonsoft.Json; | ||
using FluentAssertions; | ||
|
||
namespace Microsoft.CodeAnalysis.Sarif | ||
{ | ||
public class RunTests | ||
{ | ||
[Fact] | ||
public void Run_ColumnKindSerializesProperly() | ||
{ | ||
// In our Windows-specific SDK, if no one has explicitly set ColumnKind, we | ||
// will set it to the windows-specific value of Utf16CodeUnits. Otherwise, | ||
// the SARIF file will pick up the ColumnKind default value of | ||
// UnicodeCodePoints, which is not appropriate for Windows frameworks. | ||
RoundTripColumnKind(persistedValue: ColumnKind.None, expectedRoundTrippedValue: ColumnKind.Utf16CodeUnits); | ||
|
||
// When explicitly set, we should always preserve that setting | ||
RoundTripColumnKind(persistedValue: ColumnKind.Utf16CodeUnits, expectedRoundTrippedValue: ColumnKind.Utf16CodeUnits); | ||
RoundTripColumnKind(persistedValue: ColumnKind.UnicodeCodePoints, expectedRoundTrippedValue: ColumnKind.UnicodeCodePoints); | ||
|
||
} | ||
|
||
private void RoundTripColumnKind(ColumnKind persistedValue, ColumnKind expectedRoundTrippedValue) | ||
{ | ||
var sarifLog = new SarifLog | ||
{ | ||
Runs = new[] | ||
{ | ||
new Run | ||
{ | ||
Tool = new Tool { Name = "Test tool"}, | ||
ColumnKind = persistedValue | ||
} | ||
} | ||
}; | ||
|
||
string json = JsonConvert.SerializeObject(sarifLog); | ||
|
||
// We should never see the default value persisted to JSON | ||
json.Contains("unicodeCodePoints").Should().BeFalse(); | ||
|
||
sarifLog = JsonConvert.DeserializeObject<SarifLog>(json); | ||
sarifLog.Runs[0].ColumnKind.Should().Be(expectedRoundTrippedValue); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
using System.Collections.Generic; | ||
using System.Runtime.Serialization; | ||
using Microsoft.CodeAnalysis.Sarif.Readers; | ||
using Newtonsoft.Json; | ||
|
||
namespace Microsoft.CodeAnalysis.Sarif | ||
{ | ||
|
@@ -138,6 +139,8 @@ public SarifNodeKind SarifNodeKind | |
/// Specifies the unit in which the tool measures columns. | ||
/// </summary> | ||
[DataMember(Name = "columnKind", IsRequired = false, EmitDefaultValue = false)] | ||
[JsonConverter(typeof(EnumConverter))] | ||
[System.ComponentModel.DefaultValue(ColumnKind.UnicodeCodePoints)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jschema doesn't emit default values currently. We'll have to manually maintain them for now. microsoft/jschema#57 |
||
public ColumnKind ColumnKind { get; set; } | ||
|
||
/// <summary> | ||
|
@@ -151,6 +154,7 @@ public SarifNodeKind SarifNodeKind | |
/// </summary> | ||
public Run() | ||
{ | ||
this.ColumnKind = ColumnKind.UnicodeCodePoints; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ok, this is an important apparent JSON.NET gap. in JSON.NET, you can provide an argument in a JSON convert settings object that specifies that default values should populate if missing on deserialization. the problem with that is that you need to explicitly create and pass the appropriate settings object (and we've attempted to make everything work with a single API call with no parameterization of this kind). to work around the problem, we provide a programmatic equivalent of populate by setting the default value in the ctor. when JSON.NET itself is deserialization, it will overwrite this property in cases where the JSON has an explicit value. a fix to jschema #57 should provide this fix as well (and not just the default value attribute that elides property values from persisted JSON if they map to the default). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume you are talking about JsonSerializerSettings.DefaultValueHandling, whose But it goes on to say "DefaultValueHandling can also be customized on individual properties with JsonPropertyAttribute." So I think we can fix this by emitting
on every property. In reply to: 239879240 [](ancestors = 239879240) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added that to https://github.com/Microsoft/jchema/issues/57. In reply to: 239910615 [](ancestors = 239910615,239879240) |
||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -656,6 +656,14 @@ | |
"unicodeCodePoints" | ||
] | ||
} | ||
}, | ||
{ | ||
"kind": "AttributeHint", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oops. test gap closed here by adding columnKind explicitly to our comprehensive test files. I manually reviewed the hints file to make sure we don't have this same mistake elsewhere. |
||
"arguments": { | ||
"namespaceName": "Newtonsoft.Json", | ||
"typeName": "JsonConverter", | ||
"arguments": [ "typeof(EnumConverter)" ] | ||
} | ||
} | ||
], | ||
"Run.Files": [ | ||
|
@@ -738,7 +746,6 @@ | |
"arguments": [ "typeof(Microsoft.CodeAnalysis.Sarif.Readers.SarifVersionConverter)" ] | ||
} | ||
} | ||
|
||
], | ||
"stack": [ | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,24 @@ public partial class Run | |
private static Invocation EmptyInvocation = new Invocation(); | ||
private static LogicalLocation EmptyLogicalLocation = new LogicalLocation(); | ||
|
||
public bool ShouldSerializeColumnKind() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Wow, I hadn't looked closely at this until now. This is the first time I wondered who was calling these, and I see that JSON.NET has a built-in mechanism for conditional serialization. |
||
{ | ||
// This serialization helper does two things. | ||
// | ||
// First, if ColumnKind has not been | ||
// explicitly set, we will set it to the value that works for the Microsoft | ||
// platform (which is not the specified SARIF default). This makes sure that | ||
// the value is set appropriate for code running on the Microsoft platform, | ||
// even if the SARIF producer is not aware of this rather obscure value. | ||
if (this.ColumnKind == ColumnKind.None) | ||
{ | ||
this.ColumnKind = ColumnKind.Utf16CodeUnits; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree with this logic, but not necessarily with its home in what should be essentially a "getter" method. It doesn't feel right for this method to have a side effect. Please spend a few moments pondering whether there's a better place for this, but don't block if you can't think of one. |
||
|
||
// Second, we do not persist this value if it is set to its default. | ||
return this.ColumnKind != ColumnKind.UnicodeCodePoints; | ||
} | ||
|
||
public bool ShouldSerializeFiles() { return this.Files != null && this.Files.Values.Any(); } | ||
|
||
public bool ShouldSerializeGraphs() { return this.Graphs != null && this.Graphs.Values.Any(); } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'w' => 'W'