Skip to content

Commit

Permalink
Added support for projects that do not enable global usings. (#518)
Browse files Browse the repository at this point in the history
* Added support for projects that do not enable global usings
  • Loading branch information
mwadams authored Dec 17, 2024
1 parent 4a6034e commit 9d8791b
Show file tree
Hide file tree
Showing 33 changed files with 6,954 additions and 7 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,29 @@ Benchmark suites for various components.

The Source Generator which generates types from Json Schema.

## V4.1.2 Updates

Added the `--addExplicitUsings` switch to the code generator (and a corresponding property to the `generator-config.json` schema). If `true`, then
the source generator will emit the standard global usings explicitly into the generated source files. You can then use the generated code in a project that does not have `<ImplicitUsings>enable</ImplicitUsings>`.

```csharp
using global::System;
using global::System.Collections.Generic;
using global::System.IO;
using global::System.Linq;
using global::System.Net.Http;
using global::System.Threading;
using global::System.Threading.Tasks;
```

## V4.1.1 Updates

## Help for people building analyzers and source generators with JSON Schema code generation

We have built a self-contained package called Corvus.Json.SourceGeneratorTools for people looking to build .NET Analyzers or Source Generators that take advantage of JSON Schema code generation.

See the [README](./Solutions/Corvus.Json.SourceGeneratorTools/README.md) for details.

## V4.1 Updates

### YAML support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ public readonly struct Namespace(JsonReference baseUri, string dotnetNamespace)
/// <param name="fileExtension">Gets the file extension to use. Defaults to <c>.cs</c>.</param>
/// <param name="useImplicitOperatorString">If true, then the string conversion will be implicit.</param>
/// <param name="lineEndSequence">The line-end sequence. Defaults to <c>\r\n</c>.</param>
/// <param name="addExplicitUsings">If true, then the generated files will include using statements for the standard implicit usings. You should use this when your project does not use implicit usings.</param>
public class Options(
string defaultNamespace,
NamedType[]? namedTypes = null,
Expand All @@ -604,7 +605,8 @@ public class Options(
string[]? disabledNamingHeuristics = null,
string fileExtension = ".cs",
bool useImplicitOperatorString = false,
string lineEndSequence = "\r\n")
string lineEndSequence = "\r\n",
bool addExplicitUsings = false)
{
private readonly FrozenDictionary<string, NamedType> namedTypeMap = namedTypes?.ToFrozenDictionary(kvp => kvp.Reference, kvp => kvp) ?? FrozenDictionary<string, NamedType>.Empty;
private readonly FrozenDictionary<string, string> namespaceMap = namespaces?.ToFrozenDictionary(kvp => kvp.BaseUri, kvp => kvp.DotnetNamespace) ?? FrozenDictionary<string, string>.Empty;
Expand All @@ -625,7 +627,7 @@ public class Options(
internal bool UseOptionalNameHeuristics { get; } = useOptionalNameHeuristics;

/// <summary>
/// Gets a value indicating whether to always assert the format validation, regardless of the vocabularyy.
/// Gets a value indicating whether to always assert the format validation, regardless of the vocabulary.
/// </summary>
internal bool AlwaysAssertFormat { get; } = alwaysAssertFormat;

Expand All @@ -640,7 +642,7 @@ public class Options(
internal string FileExtension { get; } = fileExtension;

/// <summary>
/// Gets a value indicating whether to generate an implict operator for conversion to <see langword="string"/>.
/// Gets a value indicating whether to generate an implicit operator for conversion to <see langword="string"/>.
/// </summary>
internal bool UseImplicitOperatorString { get; } = useImplicitOperatorString;

Expand All @@ -654,6 +656,14 @@ public class Options(
/// </summary>
internal HashSet<string> DisabledNamingHeuristics { get; } = disabledNamingHeuristics is string[] n ? [.. n] : [];

/// <summary>
/// Gets a value indicating whether to include using statements for the standard implicit usings.
/// </summary>
/// <remarks>
/// You should use this when your project does not use implicit usings.
/// </remarks>
internal bool AddExplicitUsings { get; } = addExplicitUsings;

/// <summary>
/// Gets the namespace for the base URI.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDecla
{
if ((typeDeclaration.ImpliedCoreTypes() & CoreTypes.Array) != 0)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

generator
.BeginFile(typeDeclaration, "Array")
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
new("System.Buffers", FrameworkType.Net80OrGreater),
"System.Collections",
"System.Collections.Immutable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDecla
{
if ((typeDeclaration.ImpliedCoreTypes() & CoreTypes.Boolean) != 0)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

generator
.BeginFile(typeDeclaration, "Boolean")
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
new("System.Collections.Immutable", EmitIfIsObjectOrArray(typeDeclaration)),
"System.Diagnostics.CodeAnalysis",
"System.Text.Json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@ private CorePartial()
/// <inheritdoc/>
public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDeclaration)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

return generator
.BeginFile(typeDeclaration, string.Empty)
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
"System.Buffers",
RequiresImmutableCollections(typeDeclaration) ? "System.Collections.Immutable" : ConditionalCodeSpecification.DoNotEmit,
"System.Runtime.CompilerServices",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDecla
{
if ((typeDeclaration.ImpliedCoreTypes() & (CoreTypes.Number | CoreTypes.Integer)) != 0)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

generator
.BeginFile(typeDeclaration, "Number")
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
new("System.Collections.Immutable", EmitIfIsObjectOrArray(typeDeclaration)),
"System.Diagnostics.CodeAnalysis",
new("System.Numerics", FrameworkType.Net80OrGreater),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDecla
{
if ((typeDeclaration.ImpliedCoreTypes() & CoreTypes.Object) != 0)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

generator
.BeginFile(typeDeclaration, "Object")
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
new("System.Collections", EmitIfIsMapObject(typeDeclaration)),
"System.Collections.Immutable",
new("System.Diagnostics.CodeAnalysis", EmitIfIsMapObject(typeDeclaration)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDecla
{
if ((typeDeclaration.ImpliedCoreTypes() & CoreTypes.String) != 0)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

generator
.BeginFile(typeDeclaration, "String")
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
"System.Buffers",
new("System.Collections.Immutable", EmitIfIsObjectOrArray(typeDeclaration)),
"System.Diagnostics.CodeAnalysis",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,22 @@ public CodeGenerator EmitFile(CodeGenerator generator, TypeDeclaration typeDecla
// If we have any validation keywords, emit the validation file.
if (typeDeclaration.ValidationKeywords().Count != 0)
{
FrameworkType addExplicitUsings = typeDeclaration.AddExplicitUsings() ? FrameworkType.All : FrameworkType.NotEmitted;

generator
.BeginFile(typeDeclaration, "Validate")
.AppendAutoGeneratedHeader()
.AppendLine()
.AppendLine("#nullable enable")
.AppendLine()
.AppendUsings(
new("global::System", addExplicitUsings),
new("global::System.Collections.Generic", addExplicitUsings),
new("global::System.IO", addExplicitUsings),
new("global::System.Linq", addExplicitUsings),
new("global::System.Net.Http", addExplicitUsings),
new("global::System.Threading", addExplicitUsings),
new("global::System.Threading.Tasks", addExplicitUsings),
"System.Runtime.CompilerServices",
"System.Text.Json",
RequiresRegularExressions(typeDeclaration) ? "System.Text.RegularExpressions" : ConditionalCodeSpecification.DoNotEmit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static class TypeDeclarationExtensions
private const string OptionalAsNullableKey = "CSharp_LanguageProvider_OptionalAsNullable";
private const string PreferredBinaryJsonNumberKindKey = "CSharp_LanguageProvider_PreferredBinaryJsonNumberKind";
private const string UseImplicitOperatorStringKey = "CSharp_LanguageProvider_UseImplicitOperatorString";
private const string AddExplicitUsingsKey = "CSharp_LanguageProvider_AddExplicitUsings";

/// <summary>
/// Sets the relevant metadata from the <see cref="CSharpLanguageProvider.Options"/>.
Expand All @@ -34,6 +35,7 @@ public static void SetCSharpOptions(this TypeDeclaration typeDeclaration, CSharp
typeDeclaration.SetMetadata(AlwaysAssertFormatKey, options.AlwaysAssertFormat);
typeDeclaration.SetMetadata(OptionalAsNullableKey, options.OptionalAsNullable);
typeDeclaration.SetMetadata(UseImplicitOperatorStringKey, options.UseImplicitOperatorString);
typeDeclaration.SetMetadata(AddExplicitUsingsKey, options.AddExplicitUsings);
}

/// <summary>
Expand Down Expand Up @@ -180,6 +182,22 @@ public static bool UseImplicitOperatorString(this TypeDeclaration typeDeclaratio
return false;
}

/// <summary>
/// Gets a value indicating whether to generate using statements for the standard implicit usings.
/// </summary>
/// <param name="typeDeclaration">The type declaration to test.</param>
/// <returns><see langword="true"/> if the using statements should be added.</returns>
public static bool AddExplicitUsings(this TypeDeclaration typeDeclaration)
{
if (typeDeclaration.TryGetMetadata(AddExplicitUsingsKey, out bool? addExplicitUsings) &&
addExplicitUsings is bool value)
{
return value;
}

return false;
}

/// <summary>
/// Determines if the given name collides with the parent name.
/// </summary>
Expand Down
8 changes: 7 additions & 1 deletion Solutions/Corvus.Json.CodeGenerator/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public sealed class Settings : CommandSettings
[Description("If true, YAML support is enabled. You may use YAML, JSON or a mixture of such documents.")]
[DefaultValue(false)]
public bool SupportYaml { get; init; }

[CommandOption("--addExplicitUsings")]
[Description("If true, the generate will include using statements for the standard implicit usings.")]
[DefaultValue(false)]
public bool AddExplicitUsings { get; init; }
}

/// <inheritdoc/>
Expand All @@ -109,7 +114,8 @@ public override Task<int> ExecuteAsync(CommandContext context, Settings settings
useSchemaValue: settings.UseSchema != SchemaVariant.NotSpecified ? (GeneratorConfig.UseSchema)settings.UseSchema.ToString() : default(GeneratorConfig.UseSchema?),
useImplicitOperatorString: settings.UseImplicitOperatorString,
useUnixLineEndings: settings.UseUnixLineEndings,
supportYaml: settings.SupportYaml);
supportYaml: settings.SupportYaml,
addExplicitUsings: settings.AddExplicitUsings);

return GenerationDriver.GenerateTypes(config);
}
Expand Down
3 changes: 2 additions & 1 deletion Solutions/Corvus.Json.CodeGenerator/GenerationDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ private static CSharpLanguageProvider.Options MapGeneratorConfigToOptions(in Gen
optionalAsNullable: (generatorConfig.OptionalAsNullableValue ?? GeneratorConfig.OptionalAsNullable.DefaultInstance).Equals(GeneratorConfig.OptionalAsNullable.EnumValues.NullOrUndefined),
disabledNamingHeuristics: generatorConfig.DisabledNamingHeuristics?.Select(n => (string)n).ToArray(),
useImplicitOperatorString: generatorConfig.UseImplicitOperatorString ?? false,
lineEndSequence: (generatorConfig.UseUnixLineEndings ?? false) ? "\n" : "\r\n");
lineEndSequence: (generatorConfig.UseUnixLineEndings ?? false) ? "\n" : "\r\n",
addExplicitUsings: generatorConfig.AddExplicitUsings ?? false);
}

private static async Task<string?> BeginMapFile(GeneratorConfig generatorConfig, string outputPath)
Expand Down
Loading

0 comments on commit 9d8791b

Please sign in to comment.