-
Notifications
You must be signed in to change notification settings - Fork 28
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
Fix #57, fix #58: Emit initializers and attributes for default values #60
Conversation
<Generator>ResXFileCodeGenerator</Generator> | ||
<LastGenOutput>Resources.Designer.cs</LastGenOutput> | ||
</EmbeddedResource> | ||
</ItemGroup> |
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.
I omitted these elements when I converted JSchema to .NET Core. Without them, the .Designer.cs file isn't regenerated when you edit the .resx file. #ByDesign
@@ -169,7 +169,7 @@ | |||
<value>An uninitialized kind.</value> | |||
</data> | |||
<data name="PropertyCtorParamDescription" xml:space="preserve"> | |||
<value>An initialization value for the <see cref="P: {0}" /> property.</value> |
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.
Fix minor existing bug in generation of doc comments for properties. #Closed
if (Usings == null) | ||
Usings = Usings ?? new List<string>(); | ||
|
||
if (!Usings.Contains(namespaceName)) |
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.
Contains [](start = 24, length = 8)
Avoid duplicates in this list. This has never been a problem; there's probably code somewhere else that de-dupes it, but I can't be troubled to look for it now. #Closed
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.
I found the code for this in SyntaxNodeExtensions.Format.
This test is not needed. Reverting.
In reply to: 244399389 [](ancestors = 244399389)
@@ -871,10 +871,43 @@ public void GeneratesCloningCode() | |||
""type"": ""integer"", | |||
""description"": ""An integer property."" | |||
}, | |||
""intPropWithDefault"": { |
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.
intPropWithDefault [](start = 6, length = 18)
Add properties with default values for all JSON schema primitive types.
""default"": 42 | ||
}, | ||
""numberProp"": { | ||
""type"": ""number"", |
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.
number [](start = 18, length = 6)
Add coverage for number
properties, which we previously lacked.
""description"": ""A string property with a default value."", | ||
""default"": ""42"" | ||
}, | ||
""boolProp"": { |
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.
boolProp [](start = 6, length = 8)
We didn't cover bool
properties before, either.
NumberPropWithDefault = 42.1; | ||
StringPropWithDefault = ""42""; | ||
BoolPropWithTrueDefault = true; | ||
BoolPropWithFalseDefault = false; | ||
} |
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.
} [](start = 8, length = 1)
Demonstrates fix for #57.
/// An integer property with a default value. | ||
/// </summary> | ||
[DataMember(Name = ""intPropWithDefault"", IsRequired = false, EmitDefaultValue = false)] | ||
[DefaultValue(42)] |
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.
[DefaultValue(42)] [](start = 8, length = 18)
Demonstrates fix for #58.
src/Json.Schema/JsonSchema.cs
Outdated
if (left is long) { return (long)left == (long)right; } | ||
if (left is double) { return (double)left == (double)right; } | ||
|
||
return left == right; // Not good. How do we deal with other types? |
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.
Not [](start = 37, length = 3)
I'm open to suggestion here. Maybe left.Equals(right)
is better, because at least it doesn't fail for two distinct objects that are value equal. #Resolved
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.
Why aren't you simply using the Object.Equals implementation entirely for this? I see via reference source that it appears to shell out to some native code for the implementation. but it should handle all this type consideration/unboxing to do its thing. All this code used to be authored as C#, which I've reviewed in past contexts.
In reply to: 244400084 [](ancestors = 244400084)
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.
Thanks for that! It didn't even occur to me. Yes, after removing this function and instead calling Object.Equals(Default, other.Default)
, all the tests still pass.
In reply to: 244415654 [](ancestors = 244415654,244400084)
@@ -279,6 +282,24 @@ protected override AttributeSyntax[] GeneratePropertyAttributes(string propertyN | |||
|
|||
attributes.Add(dataMemberAttribute); | |||
|
|||
if (defaultValue != null) |
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.
if [](start = 12, length = 2)
Implements fix for #58 (DefaultValue
attributes).
@@ -372,7 +393,7 @@ private ConstructorDeclarationSyntax GenerateDefaultConstructor() | |||
{ | |||
return SyntaxFactory.ConstructorDeclaration(SuffixedTypeName) | |||
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) | |||
.AddBodyStatements() | |||
.AddBodyStatements(GenerateDefaultInitializations()) |
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.
GenerateDefaultInitializations [](start = 35, length = 30)
Implements fix for #57.
// integer properties as Int64 (long), but we generate integer properties | ||
// with type int. The extra cast causes Roslyn to emit the literal 42, | ||
// which can be assigned to an int, rather than 42L, which cannot. We should | ||
// consider changing the code generation to emit longs for integer properties. |
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.
. [](start = 93, length = 1)
Michael, your thoughts on this? #Pending
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.
I see. Yes, the problem is ours, JSON/JavaScript number/integer primitives clearly allow for values that exceed C# Int32. If we're going to make this change we should do it soon! It will be an intrusive breaking change. Since we're expanding the type size, the impact should be minimal beyond the literal type change. But it's worrisome to think about the OM signature impact here.
In reply to: 244400469 [](ancestors = 244400469)
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.
Michael argued that .NET pervasively uses int
(not long
, not even uint)
in its APIs -- and that for historical reasons (because VB didn't support "unsigned"). We need to think hard about this, and assess the impact on interoperability with other .NET APIs, before deciding to do this. Won't do it now.
In reply to: 244417379 [](ancestors = 244417379,244400469)
@michaelcfanning is added to the review. #Closed |
@EasyRhinoMSFT is added to the review. #Closed |
ExpressionStatementSyntax initializationStatement = GenerateDefaultInitialization(propertyName, defaultValue); | ||
if (initializationStatement != null) | ||
{ | ||
initializations.Add(GenerateDefaultInitialization(propertyName, defaultValue)); |
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.
GenerateDefaultInitialization(propertyName, defaultValue [](start = 44, length = 56)
can't you just provide 'initializationStatement' here as an arg? #Resolved
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.
Oh good heavens. I can't quite believe I did that! Fixed.
In reply to: 244416058 [](ancestors = 244416058)
} | ||
} | ||
|
||
return initializations.ToArray(); |
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.
initializations.ToArray(); [](start = 19, length = 26)
why are you taking the memory costs to generate an array? mostly a List is appropriate for places where an array would be (a List can be enumerated, for example). an Array, in fact, is less flexible than a list, as its size isn't mutable. #Pending
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.
Because of the shape of the Roslyn API. The result of this method call will be passed to Roslyns ConstructorDeclarationSyntax.AddBodyStatements
, which takes an argument of type params StatementSyntax[]
. I'll see if there's a way to add the statements that doesn't require the array. Hang on...
In reply to: 244416245 [](ancestors = 244416245)
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.
This is a much discussed feature gap in C#. See dotnet/csharplang#179 and dotnet/roslyn#36
In reply to: 244418426 [](ancestors = 244418426,244416245)
|
||
if (value is bool) | ||
{ | ||
SyntaxKind literalSyntaxKind = (bool)value == true |
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.
== true [](start = 59, length = 7)
you don't actually need the '== true; here, the bool is sufficient to drive the ternary conditional #WontFix
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.
I know, I waffled on that, but I decided to write it this way because of the way it reads in English: "If the value is true
, use a TrueLiteralExpression
, otherwise use a FalseLiteralExpression
. The "== true
" reads as "is true" in my head.
In reply to: 244416578 [](ancestors = 244416578)
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.
i don't know what it is but I mostly find that direct comparisons to bool literals compromise rather than improve readability. this one's a corner case.
In reply to: 244417679 [](ancestors = 244417679,244416578)
@@ -103,7 +103,7 @@ private PropertyDeclarationSyntax CreatePropertyDeclaration(string propertyName) | |||
.AddModifiers(GeneratePropertyModifiers(propertyName)) | |||
.AddAccessorListAccessors(GeneratePropertyAccessors()); | |||
|
|||
AttributeSyntax[] attributes = GeneratePropertyAttributes(propertyName, info.SerializedName, info.IsRequired); | |||
AttributeSyntax[] attributes = GeneratePropertyAttributes(propertyName, info.SerializedName, info.IsRequired, info.DefaultValue); |
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.
AttributeSyntax[] [](start = 12, length = 17)
I will stop remarking on the point, but the pervasive use of arrays in this code base result in unnecessary memory allocations. #Pending
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.
Yes, as I mentioned in response to your other comment, many Roslyn APIs take a params Thing[]
. I'll see if there is another set of Roslyn APIs that use a better pattern.
In reply to: 244417551 [](ancestors = 244417551)
@@ -871,10 +871,43 @@ public void GeneratesCloningCode() | |||
""type"": ""integer"", | |||
""description"": ""An integer property."" | |||
}, | |||
""intPropWithDefault"": { |
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.
intPropWithDefault [](start = 6, length = 18)
I find the jschema test strategy unsatisfying in that there's no clear marker in tests of the real world impact of changes. Perhaps you could add some single practical example of the framework in use that would illustrate more effectively how updates alter code emit. TBD
Actually, there doesn't seem to be an In reply to: 450436604 [](ancestors = 450436604,450435940) Refers to: src/Json.Schema.ToDotNet/TypeGenerator.cs:40 in 38d8c45. [](commit_id = 38d8c45, deletion_comment = False) |
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.
* Remove dependency on RuleMessageId (shortly to be deprecated) (#24) * Release 0.57.0 * Todotnet package (#30) * Add utilities to validation and todotnet packages * Update nuget.exe. Add success message for nuspec-driven package creation. * Update signing scripts * Release 0.58.0 (#32) * Address PR feedback from 0.58.0 release (#33) Also: * Build the `develop` branch in AppVeyor. * Add dotnet publish step to build. (#34) Also: - Use `.CommandLine`, not `.Cli` in the command line tool namespaces. * Disable appveyor email notifications (#36) * Update to latest SARIF SDK + JSON.NET downgrade (#38) * Fix ToDotNet.Cli project file so resources can be found. (#40) When you run the ToDotNet command line app, it fails because it can't find the resource stream. Somehow the `.resx` and the `.Resources.designer` files got separated in the project file. Fix the project file to connect the resources back up. * Rewrite top-level README in preparation for documenting each component. (#47) An almost complete rewrite. * Fix #41: Document Json.Pointer (#48) * embed resx resources (#53) * Add unit test to demonstrate interaction of ClassNameHint with other hints (#54) * Start codegen README (#59) * Fix #57, fix #58: Emit initializers and attributes for default values (#60) #57: When a schema property declares a default value, assign that value to the property in the default constructor. This ensures that the property has the correct default even if you construct the object by hand rather than by deserializing it from JSON. #58: When a schema property declares a default value, decorate the property with a `System.ComponentModel.DefaultValue` attribute. This avoids your having to specify those attributes in the CodeGenHints.json file, which is error-prone. Also: - Fix a bug where the generated doc comments for properties had an extra space: `cref="P: propName`" → `cref="P:propName`". - Address some hygiene-related IDE messages by marking some properties `readonly`, and by inlining the declarations of some `out` parameters. * Fix #61: Generate JsonProperty attribute for properties with defaults (#62) * Add final release note in advance of shipping 0.59.0
* Remove dependency on RuleMessageId (shortly to be deprecated) (#24) * Release 0.57.0 * Todotnet package (#30) * Add utilities to validation and todotnet packages * Update nuget.exe. Add success message for nuspec-driven package creation. * Update signing scripts * Release 0.58.0 (#32) * Address PR feedback from 0.58.0 release (#33) Also: * Build the `develop` branch in AppVeyor. * Add dotnet publish step to build. (#34) Also: - Use `.CommandLine`, not `.Cli` in the command line tool namespaces. * Disable appveyor email notifications (#36) * Update to latest SARIF SDK + JSON.NET downgrade (#38) * Fix ToDotNet.Cli project file so resources can be found. (#40) When you run the ToDotNet command line app, it fails because it can't find the resource stream. Somehow the `.resx` and the `.Resources.designer` files got separated in the project file. Fix the project file to connect the resources back up. * Rewrite top-level README in preparation for documenting each component. (#47) An almost complete rewrite. * Fix #41: Document Json.Pointer (#48) * embed resx resources (#53) * Add unit test to demonstrate interaction of ClassNameHint with other hints (#54) * Start codegen README (#59) * Fix #57, fix #58: Emit initializers and attributes for default values (#60) #57: When a schema property declares a default value, assign that value to the property in the default constructor. This ensures that the property has the correct default even if you construct the object by hand rather than by deserializing it from JSON. #58: When a schema property declares a default value, decorate the property with a `System.ComponentModel.DefaultValue` attribute. This avoids your having to specify those attributes in the CodeGenHints.json file, which is error-prone. Also: - Fix a bug where the generated doc comments for properties had an extra space: `cref="P: propName`" → `cref="P:propName`". - Address some hygiene-related IDE messages by marking some properties `readonly`, and by inlining the declarations of some `out` parameters. * Fix #61: Generate JsonProperty attribute for properties with defaults (#62) * Add final release note in advance of shipping 0.59.0 * Update licensing reference to satisfy NuGet improvements in this area. (#65)
#57: When a schema property declares a default value, assign that value to the property in the default constructor. This ensures that the property has the correct default even if you construct the object by hand rather than by deserializing it from JSON.
#58: When a schema property declares a default value, decorate the property with a
System.ComponentModel.DefaultValue
attribute. This avoids your having to specify those attributes in the CodeGenHints.json file, which is error-prone.Also:
cref="P: propName
" →cref="P:propName
".readonly
, and by inlining the declarations of someout
parameters.