diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/RoundTripModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/RoundTripModel.cs index b8e08700c3..ea946b94dd 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/RoundTripModel.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/RoundTripModel.cs @@ -27,28 +27,28 @@ public partial class RoundTripModel public Thing RequiredModel { get; set; } /// this is an int based extensible enum. - public string IntExtensibleEnum { get; set; } + public int IntExtensibleEnum { get; set; } /// this is a collection of int based extensible enum. - public IList IntExtensibleEnumCollection { get; } + public IList IntExtensibleEnumCollection { get; } /// this is a float based extensible enum. - public string FloatExtensibleEnum { get; set; } + public int FloatExtensibleEnum { get; set; } /// this is a collection of float based extensible enum. - public IList FloatExtensibleEnumCollection { get; } + public IList FloatExtensibleEnumCollection { get; } /// this is a float based fixed enum. - public string FloatFixedEnum { get; set; } + public float FloatFixedEnum { get; set; } /// this is a collection of float based fixed enum. - public IList FloatFixedEnumCollection { get; } + public IList FloatFixedEnumCollection { get; } /// this is a int based fixed enum. - public string IntFixedEnum { get; set; } + public int IntFixedEnum { get; set; } /// this is a collection of int based fixed enum. - public IList IntFixedEnumCollection { get; } + public IList IntFixedEnumCollection { get; } /// this is a string based fixed enum. public string StringFixedEnum { get; set; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/Thing.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/Thing.cs index 993943fa6e..9bcec46eeb 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/Thing.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.TestProjects/Unbranded-TypeSpec/src/Generated/Models/Thing.cs @@ -21,10 +21,10 @@ public partial class Thing public string RequiredLiteralString { get; } = "accept"; /// required literal int. - public string RequiredLiteralInt { get; set; } = "123"; + public int RequiredLiteralInt { get; } = 123; /// required literal float. - public string RequiredLiteralFloat { get; set; } = "1.23"; + public float RequiredLiteralFloat { get; } = 1.23F; /// required literal bool. public bool RequiredLiteralBool { get; } = false; @@ -33,10 +33,10 @@ public partial class Thing public string OptionalLiteralString { get; set; } /// optional literal int. - public string OptionalLiteralInt { get; set; } + public int OptionalLiteralInt { get; set; } /// optional literal float. - public string OptionalLiteralFloat { get; set; } + public float OptionalLiteralFloat { get; set; } /// optional literal bool. public bool OptionalLiteralBool { get; set; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/ScmTypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/ScmTypeFactory.cs index dcb4613362..f36ebb7ece 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/ScmTypeFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/ScmTypeFactory.cs @@ -30,7 +30,8 @@ internal class ScmTypeFactory : TypeFactory //InputEnumType enumType => ClientModelPlugin.Instance.OutputLibrary.EnumMappings.TryGetValue(enumType, out var provider) //? provider.Type.WithNullable(inputType.IsNullable) //: throw new InvalidOperationException($"No {nameof(EnumType)} has been created for `{enumType.Name}` {nameof(InputEnumType)}."), - InputEnumType enumType => new CSharpType(typeof(string), inputType.IsNullable), + // TODO -- this is temporary until we have support for enums + InputEnumType enumType => CreateCSharpType(enumType.EnumValueType).WithNullable(enumType.IsNullable), InputModelType model => ClientModelPlugin.Instance.OutputLibrary.ModelMappings.TryGetValue(model, out var provider) ? provider.Type.WithNullable(inputType.IsNullable) : new CSharpType(typeof(object), model.IsNullable).WithNullable(inputType.IsNullable), diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CSharpType.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CSharpType.cs index 5a6ee04e11..7374e30f33 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CSharpType.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CSharpType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -25,9 +25,9 @@ public class CSharpType private bool _isEnum; private bool _isNullable; private bool _isPublic; - private bool? _isUnion; private IReadOnlyList _arguments; - private IReadOnlyList _unionItemTypes; + private object? _literal; + private IReadOnlyList? _unionItemTypes; private bool? _isReadOnlyMemory; private bool? _isList; @@ -143,7 +143,6 @@ internal CSharpType(TypeProvider implementation, bool isValueType = false, bool [MemberNotNull(nameof(_namespace))] [MemberNotNull(nameof(_arguments))] [MemberNotNull(nameof(_isPublic))] - [MemberNotNull(nameof(_unionItemTypes))] private void Initialize(string? name, bool isValueType, bool isEnum, bool isNullable, string? ns, CSharpType? declaringType, IReadOnlyList? args, bool isPublic) { @@ -155,7 +154,6 @@ private void Initialize(string? name, bool isValueType, bool isEnum, bool isNull _declaringType = declaringType; _arguments = args ?? Array.Empty(); _isPublic = isPublic; - _unionItemTypes ??= Array.Empty(); } public string Namespace { get { return _namespace; } } @@ -163,15 +161,15 @@ private void Initialize(string? name, bool isValueType, bool isEnum, bool isNull public CSharpType? DeclaringType { get { return _declaringType; } } public bool IsValueType { get { return _isValueType; } } public bool IsEnum { get { return _isEnum; } } - public bool IsLiteral => Literal is not null; - public bool IsUnion => _isUnion ??= UnionItemTypes.Count > 0; + public bool IsLiteral => _literal is not null; + public bool IsUnion => _unionItemTypes?.Count > 0; public bool IsPublic { get { return _isPublic; } } public bool IsFrameworkType => _type != null; public bool IsNullable { get { return _isNullable; } } public bool IsGenericType => Arguments.Count > 0; public bool IsCollection => _isCollection ??= TypeIsCollection(); public Type FrameworkType => _type ?? throw new InvalidOperationException("Not a framework type"); - public Constant? Literal { get; private init; } + public object Literal => _literal ?? throw new InvalidOperationException("Not a literal type"); internal TypeProvider Implementation => _implementation ?? throw new InvalidOperationException($"Not implemented type: '{Namespace}.{Name}'"); public IReadOnlyList Arguments { get { return _arguments; } } public CSharpType InitializationType => _initializationType ??= GetImplementationType(); @@ -180,7 +178,7 @@ private void Initialize(string? name, bool isValueType, bool isEnum, bool isNull public CSharpType InputType => _inputType ??= GetInputType(); public CSharpType OutputType => _outputType ??= GetOutputType(); public Type? SerializeAs { get; init; } - public IReadOnlyList UnionItemTypes { get { return _unionItemTypes; } private init { _unionItemTypes = value; } } + public IReadOnlyList UnionItemTypes => _unionItemTypes ?? throw new InvalidOperationException("Not a union type"); private bool TypeIsReadOnlyMemory() => IsFrameworkType && _type == typeof(ReadOnlyMemory<>); @@ -471,11 +469,18 @@ public CSharpType GetGenericTypeDefinition() /// /// Flag to determine if the new type is nullable. /// The existing if it is nullable, otherwise a new instance of . - public CSharpType WithNullable(bool isNullable) => - isNullable == IsNullable ? this : IsFrameworkType + public CSharpType WithNullable(bool isNullable) + { + var type = isNullable == IsNullable ? this : IsFrameworkType ? new CSharpType(FrameworkType, Arguments, isNullable) : new CSharpType(Implementation, isValueType: IsValueType, isEnum: IsEnum, isNullable: isNullable, arguments: Arguments, declaringType: DeclaringType, ns: Namespace, name: Name); + type._literal = _literal; + type._unionItemTypes = _unionItemTypes; + + return type; + } + public static implicit operator CSharpType(Type type) => new CSharpType(type); public sealed override string ToString() @@ -541,20 +546,10 @@ public static CSharpType FromLiteral(CSharpType type, object literalValue) { if (type.IsFrameworkType) { - Constant? literal; - try - { - literal = new Constant(literalValue, type); - } - catch - { - literal = null; - } + var literalType = new CSharpType(type.FrameworkType, type.IsNullable); + literalType._literal = literalValue; - return new CSharpType(type.FrameworkType, type.IsNullable) - { - Literal = literal - }; + return literalType; } throw new NotSupportedException("Literals are not supported in non-framework type"); @@ -568,10 +563,10 @@ public static CSharpType FromLiteral(CSharpType type, object literalValue) /// A instance representing those unioned types. public static CSharpType FromUnion(IReadOnlyList unionItemTypes, bool isNullable) { - return new CSharpType(typeof(BinaryData), isNullable) - { - UnionItemTypes = unionItemTypes - }; + var type = new CSharpType(typeof(BinaryData), isNullable); + type._unionItemTypes = unionItemTypes; + + return type; } public CSharpType MakeGenericType(IReadOnlyList arguments) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/CodeBlocks/DiagnosticScopeMethodBodyBlock.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/CodeBlocks/DiagnosticScopeMethodBodyBlock.cs index 15425c72af..27345fe440 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/CodeBlocks/DiagnosticScopeMethodBodyBlock.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/CodeBlocks/DiagnosticScopeMethodBodyBlock.cs @@ -1,7 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.Generator.CSharp.Expressions { - public sealed record DiagnosticScopeMethodBodyBlock(Diagnostic Diagnostic, Reference ClientDiagnosticsReference, MethodBodyStatement InnerStatement) : MethodBodyStatement; + public sealed record DiagnosticScopeMethodBodyBlock(Diagnostic Diagnostic, ValueExpression ClientDiagnosticsReference, MethodBodyStatement InnerStatement) : MethodBodyStatement; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/Snippets.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/Snippets.cs index 58d9998142..8c63311f9d 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/Snippets.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/Snippets.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -14,6 +14,9 @@ public static partial class Snippets public static MethodBodyStatement AsStatement(this IEnumerable statements) => statements.ToArray(); public static ValueExpression Dash { get; } = new KeywordExpression("_", null); + + public static ValueExpression DefaultOf(CSharpType type) => type is { IsValueType: true, IsNullable: false } ? Default.CastTo(type) : Null.CastTo(type); + public static ValueExpression Default { get; } = new KeywordExpression("default", null); public static ValueExpression Null { get; } = new KeywordExpression("null", null); public static ValueExpression This { get; } = new KeywordExpression("this", null); @@ -21,8 +24,8 @@ public static partial class Snippets public static BoolExpression False { get; } = new(new KeywordExpression("false", null)); public static BoolExpression Bool(bool value) => value ? True : False; - public static IntExpression Int(int value) => new IntExpression(Literal(value)); - public static LongExpression Long(long value) => new LongExpression(Literal(value)); + public static IntExpression Int(int value) => new(Literal(value)); + public static LongExpression Long(long value) => new(Literal(value)); public static ValueExpression Float(float value) => new FormattableStringToExpression($"{value}f"); public static ValueExpression Double(double value) => new FormattableStringToExpression($"{value}d"); @@ -46,7 +49,7 @@ public static ValueExpression RemoveAllNullConditional(ValueExpression expressio public static TypedValueExpression RemoveAllNullConditional(TypedValueExpression expression) => expression with { Untyped = RemoveAllNullConditional(expression.Untyped) }; - public static ValueExpression Literal(object? value) => new FormattableStringToExpression($"{value:L}"); + public static ValueExpression Literal(object? value) => new LiteralExpression(value); public static StringExpression Literal(string? value) => new(value is null ? Null : new StringLiteralExpression(value, false)); public static StringExpression LiteralU8(string value) => new(new StringLiteralExpression(value, true)); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/BoolExpression.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/BoolExpression.cs index b0e460c653..01201e0300 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/BoolExpression.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/BoolExpression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.Generator.CSharp.Expressions @@ -9,9 +9,9 @@ public sealed record BoolExpression(ValueExpression Untyped) : TypedValueExpress public BoolExpression And(ValueExpression other) => new(new BinaryOperatorExpression(" && ", this, other)); - public static BoolExpression True => new(new ConstantExpression(new Constant(true, typeof(bool)))); + public static BoolExpression True { get; } = Snippets.True; - public static BoolExpression False => new(new ConstantExpression(new Constant(false, typeof(bool)))); + public static BoolExpression False { get; } = Snippets.False; public static BoolExpression Is(ValueExpression untyped, CSharpType comparisonType) => new(new BinaryOperatorExpression("is", untyped, comparisonType)); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ConstantExpression.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ConstantExpression.cs deleted file mode 100644 index af60b44293..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ConstantExpression.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Generator.CSharp.Expressions -{ - public sealed record ConstantExpression(Constant Constant) : TypedValueExpression(Constant.Type, new UntypedConstantExpression(Constant)) - { - private record UntypedConstantExpression(Constant Constant) : ValueExpression - { - public override void Write(CodeWriter writer) => writer.Append(Constant.GetConstantFormattable()); - } - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/LiteralExpression.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/LiteralExpression.cs new file mode 100644 index 0000000000..ee0fbbf5fe --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/LiteralExpression.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Generator.CSharp.Expressions +{ + /// + /// Represents a literal expression. + /// + /// The literal value. + public sealed record LiteralExpression(object? Literal) : ValueExpression + { + public override void Write(CodeWriter writer) + { + writer.WriteLiteral(Literal); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/TypedValueExpression.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/TypedValueExpression.cs index b79d267e61..0e691e2f0a 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/TypedValueExpression.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/TypedValueExpression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -17,7 +17,7 @@ public abstract record TypedValueExpression(CSharpType Type, ValueExpression Unt public static implicit operator TypedValueExpression(FieldDeclaration name) => new VariableReference(name.Type, name.Declaration); public static implicit operator TypedValueExpression(Parameter parameter) => new ParameterReference(parameter); - public TypedValueExpression NullableStructValue() => this is not ConstantExpression && Type is { IsNullable: true, IsValueType: true } ? new TypedMemberExpression(this, nameof(Nullable.Value), Type.WithNullable(false)) : this; + public TypedValueExpression NullableStructValue() => Type is { IsNullable: true, IsValueType: true } ? new TypedMemberExpression(this, nameof(Nullable.Value), Type.WithNullable(false)) : this; public TypedValueExpression NullConditional() => Type.IsNullable ? new TypedNullConditionalExpression(this) : this; diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ValueExpression.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ValueExpression.cs index 92f7d71f6a..0583d6a350 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ValueExpression.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Expressions/ValueExpressions/ValueExpression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -20,7 +20,7 @@ public virtual void Write(CodeWriter writer) { } public static implicit operator ValueExpression(FieldDeclaration name) => new VariableReference(name.Type, name.Declaration); public static implicit operator ValueExpression(PropertyDeclaration name) => new VariableReference(name.PropertyType, name.Declaration); - public ValueExpression NullableStructValue(CSharpType candidateType) => this is not ConstantExpression && candidateType is { IsNullable: true, IsValueType: true } ? new MemberExpression(this, nameof(Nullable.Value)) : this; + public ValueExpression NullableStructValue(CSharpType candidateType) => candidateType is { IsNullable: true, IsValueType: true } ? new MemberExpression(this, nameof(Nullable.Value)) : this; public StringExpression InvokeToString() => new(Invoke(nameof(ToString))); public ValueExpression InvokeGetType() => Invoke(nameof(GetType)); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/KnownParameters.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/KnownParameters.cs index f15e8729c0..090e16fe0c 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/KnownParameters.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/KnownParameters.cs @@ -1,9 +1,10 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Runtime.CompilerServices; using System.Threading; +using Microsoft.Generator.CSharp.Expressions; using Microsoft.Generator.CSharp.Input; namespace Microsoft.Generator.CSharp @@ -24,8 +25,8 @@ public KnownParameters(TypeFactory typeFactory) public Parameter TokenAuth => new("tokenCredential", $"The token credential to copy", TypeFactory.TokenCredentialType(), null, ValidationType.None, null); public Parameter PageSizeHint => new("pageSizeHint", $"The number of items per {TypeFactory.PageResponseType():C} that should be requested (from service operations that support it). It's not guaranteed that the value will be respected.", new CSharpType(typeof(int), true), null, ValidationType.None, null); - public Parameter MatchConditionsParameter => new("matchConditions", $"The content to send as the request conditions of the request.", TypeFactory.MatchConditionsType(), Constant.Default(TypeFactory.RequestConditionsType()), ValidationType.None, null, RequestLocation: RequestLocation.Header); - public Parameter RequestConditionsParameter => new("requestConditions", $"The content to send as the request conditions of the request.", TypeFactory.RequestConditionsType(), Constant.Default(TypeFactory.RequestConditionsType()), ValidationType.None, null, RequestLocation: RequestLocation.Header); + public Parameter MatchConditionsParameter => new("matchConditions", $"The content to send as the request conditions of the request.", TypeFactory.MatchConditionsType(), Snippets.DefaultOf(TypeFactory.RequestConditionsType()), ValidationType.None, null, RequestLocation: RequestLocation.Header); + public Parameter RequestConditionsParameter => new("requestConditions", $"The content to send as the request conditions of the request.", TypeFactory.RequestConditionsType(), Snippets.DefaultOf(TypeFactory.RequestConditionsType()), ValidationType.None, null, RequestLocation: RequestLocation.Header); public static readonly Parameter ClientDiagnostics = new("clientDiagnostics", $"The handler for diagnostic messaging in the client.", new CSharpType(CodeModelPlugin.Instance.Configuration.ApiTypes.ClientDiagnosticsType), null, ValidationType.AssertNotNull, null); public static readonly Parameter Pipeline = new("pipeline", $"The HTTP pipeline for sending and receiving REST requests and responses", new CSharpType(CodeModelPlugin.Instance.Configuration.ApiTypes.HttpPipelineType), null, ValidationType.AssertNotNull, null); @@ -37,11 +38,11 @@ public KnownParameters(TypeFactory typeFactory) public static readonly Parameter RequestContent = new("content", $"The content to send as the body of the request.", RequestContentType, null, ValidationType.AssertNotNull, null, RequestLocation: RequestLocation.Body); public static readonly Parameter RequestContentNullable = new("content", $"The content to send as the body of the request.", RequestContentNullableType, null, ValidationType.None, null, RequestLocation: RequestLocation.Body); - public static readonly Parameter RequestContext = new("context", $"The request context, which can override default behaviors of the client pipeline on a per-call basis.", RequestContextNullableType, Constant.Default(RequestContextNullableType), ValidationType.None, null); + public static readonly Parameter RequestContext = new("context", $"The request context, which can override default behaviors of the client pipeline on a per-call basis.", RequestContextNullableType, Snippets.DefaultOf(RequestContextNullableType), ValidationType.None, null); public static readonly Parameter RequestContextRequired = new("context", $"The request context, which can override default behaviors of the client pipeline on a per-call basis.", RequestContextType, null, ValidationType.None, null); - public static readonly Parameter CancellationTokenParameter = new("cancellationToken", $"The cancellation token to use", new CSharpType(typeof(CancellationToken)), Constant.NewInstanceOf(typeof(CancellationToken)), ValidationType.None, null); - public static readonly Parameter EnumeratorCancellationTokenParameter = new("cancellationToken", $"Enumerator cancellation token", typeof(CancellationToken), Constant.NewInstanceOf(typeof(CancellationToken)), ValidationType.None, null) { Attributes = new[] { new CSharpAttribute(typeof(EnumeratorCancellationAttribute)) } }; + public static readonly Parameter CancellationTokenParameter = new("cancellationToken", $"The cancellation token to use", new CSharpType(typeof(CancellationToken)), Snippets.DefaultOf(typeof(CancellationToken)), ValidationType.None, null); + public static readonly Parameter EnumeratorCancellationTokenParameter = new("cancellationToken", $"Enumerator cancellation token", typeof(CancellationToken), Snippets.DefaultOf(typeof(CancellationToken)), ValidationType.None, null) { Attributes = new[] { new CSharpAttribute(typeof(EnumeratorCancellationAttribute)) } }; public static readonly Parameter Response = new("response", $"Response returned from backend service", ResponseType, null, ValidationType.None, null); } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/AutoPropertyBody.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/AutoPropertyBody.cs index 939c02dd94..698b3e4c7f 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/AutoPropertyBody.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/AutoPropertyBody.cs @@ -1,9 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.Generator.CSharp.Expressions; namespace Microsoft.Generator.CSharp { - internal record AutoPropertyBody(bool HasSetter, MethodSignatureModifiers SetterModifiers = MethodSignatureModifiers.None, ConstantExpression? InitializationExpression = null) : PropertyBody; + internal record AutoPropertyBody(bool HasSetter, MethodSignatureModifiers SetterModifiers = MethodSignatureModifiers.None, ValueExpression? InitializationExpression = null) : PropertyBody; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Constant.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Constant.cs deleted file mode 100644 index e06b1f283c..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Constant.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -using System; -using System.Globalization; - -namespace Microsoft.Generator.CSharp -{ - public readonly struct Constant - { - internal static object NewInstanceSentinel { get; } = new object(); - - public Constant(object? value, CSharpType type) - { - Value = value; - Type = type; - - if (value == null) - { - if (!type.IsNullable) - { - throw new InvalidOperationException($"Null constant with non-nullable type {type}"); - } - } - - if (value == NewInstanceSentinel || value is Expression) - { - return; - } - - // TODO: Re-enable this check once we have enum type support - //if (!type.IsFrameworkType && - // type.Implementation is EnumType && - // value != null && - // !(value is EnumTypeValue || value is string)) - //{ - // throw new InvalidOperationException($"Unexpected value '{value}' for enum type '{type}'"); - //} - - if (value != null && type.IsFrameworkType && value.GetType() != type.FrameworkType) - { - throw new InvalidOperationException($"Constant type mismatch. Value type is '{value.GetType()}'. CSharpType is '{type}'."); - } - } - - public object? Value { get; } - public CSharpType Type { get; } - public bool IsNewInstanceSentinel => Value == NewInstanceSentinel; - - internal static Constant NewInstanceOf(CSharpType type) - { - return new Constant(NewInstanceSentinel, type); - } - - public static Constant Parse(object? value, CSharpType type) - { - object? normalizedValue; - - if (!type.IsFrameworkType && type.Implementation is EnumType enumType) - { - return Constant.Default(type); - - // Uncomment this when the enums are implemented: https://github.com/Azure/autorest.csharp/issues/4579 - //if (value == null) - //{ - // return Constant.Default(type); - //} - - //var stringValue = Convert.ToString(value); - //var enumTypeValue = enumType.Values.SingleOrDefault(v => v.Value.Value?.ToString() == stringValue); - - //// Fallback to the string value if we can't find an appropriate enum member (would work only for extensible enums) - //return new Constant((object?)enumTypeValue ?? stringValue, type); - } - - Type? frameworkType = type.FrameworkType; - if (frameworkType == null) - { - throw new InvalidOperationException("Only constants of framework type and enums are allowed"); - } - - if (frameworkType == typeof(byte[]) && value is string base64String) - normalizedValue = Convert.FromBase64String(base64String); - else if (frameworkType == typeof(BinaryData) && value is string base64String2) - normalizedValue = BinaryData.FromBytes(Convert.FromBase64String(base64String2)); - else if (frameworkType == typeof(DateTimeOffset) && value is string dateTimeString) - normalizedValue = DateTimeOffset.Parse(dateTimeString, styles: DateTimeStyles.AssumeUniversal); - else - normalizedValue = Convert.ChangeType(value, frameworkType); - - return new Constant(normalizedValue, type); - } - - internal static Constant FromExpression(FormattableString expression, CSharpType type) => new Constant(new Expression(expression), type); - - internal static Constant Default(CSharpType type) - => type.IsValueType && !type.IsNullable ? new Constant(NewInstanceSentinel, type) : new Constant(null, type); - - /// - /// A value type. It represents an expression without any reference (e.g. 'DateTimeOffset.Now') - /// which looks like a constant. - /// - internal class Expression - { - internal Expression(FormattableString expressionValue) - { - ExpressionValue = expressionValue; - } - - public FormattableString ExpressionValue { get; } - } - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/ConstructorInitializer.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/ConstructorInitializer.cs index 19f93fa794..f424e8f4ec 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/ConstructorInitializer.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/ConstructorInitializer.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; using Microsoft.Generator.CSharp.Expressions; namespace Microsoft.Generator.CSharp @@ -11,7 +10,5 @@ namespace Microsoft.Generator.CSharp public sealed record ConstructorInitializer(bool IsBase, IReadOnlyList Arguments) { public ConstructorInitializer(bool isBase, IEnumerable arguments) : this(isBase, arguments.Select(p => (ValueExpression)p).ToArray()) { } - - public ConstructorInitializer(bool isBase, IEnumerable arguments) : this(isBase, arguments.Select(r => new FormattableStringToExpression(FormattableStringFactory.Create("{0:I}", r.Name))).ToArray()) { } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Parameter.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Parameter.cs index 784b044056..0bc7ba0391 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Parameter.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Parameter.cs @@ -1,14 +1,15 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Microsoft.Generator.CSharp.Expressions; using Microsoft.Generator.CSharp.Input; namespace Microsoft.Generator.CSharp { - public sealed record Parameter(string Name, FormattableString? Description, CSharpType Type, Constant? DefaultValue, ValidationType Validation, FormattableString? Initializer, bool IsApiVersionParameter = false, bool IsEndpoint = false, bool IsResourceIdentifier = false, bool SkipUrlEncoding = false, RequestLocation RequestLocation = RequestLocation.None, SerializationFormat SerializationFormat = SerializationFormat.Default, bool IsPropertyBag = false, bool IsRef = false, bool IsOut = false) + public sealed record Parameter(string Name, FormattableString? Description, CSharpType Type, ValueExpression? DefaultValue, ValidationType Validation, ValueExpression? Initializer, bool IsApiVersionParameter = false, bool IsEndpoint = false, bool IsResourceIdentifier = false, bool SkipUrlEncoding = false, RequestLocation RequestLocation = RequestLocation.None, SerializationFormat SerializationFormat = SerializationFormat.Default, bool IsPropertyBag = false, bool IsRef = false, bool IsOut = false) { internal bool IsRawData { get; init; } internal static IEqualityComparer TypeAndNameEqualityComparer = new ParameterTypeAndNameEqualityComparer(); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/DiagnosticAttribute.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/DiagnosticAttribute.cs index b012766d16..737717cac4 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/DiagnosticAttribute.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/DiagnosticAttribute.cs @@ -1,17 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Microsoft.Generator.CSharp.Expressions; + namespace Microsoft.Generator.CSharp { public class DiagnosticAttribute { - public DiagnosticAttribute(string name, ReferenceOrConstant value) + public DiagnosticAttribute(string name, ValueExpression value) { Name = name; Value = value; } public string Name { get; } - public ReferenceOrConstant Value { get; } + public ValueExpression Value { get; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/Reference.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/Reference.cs deleted file mode 100644 index 84106a3ef4..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/Reference.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Generator.CSharp -{ - public readonly struct Reference - { - public Reference(string name, CSharpType type) - { - Name = name; - Type = type; - } - - public string Name { get; } - public CSharpType Type { get; } - - public static implicit operator Reference(Parameter parameter) => new Reference(parameter.Name, parameter.Type); - public static implicit operator Reference(FieldDeclaration field) => new Reference(field.Name, field.Type); - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/ReferenceOrConstant.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/ReferenceOrConstant.cs deleted file mode 100644 index 04e0be97e9..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Requests/ReferenceOrConstant.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Microsoft.Generator.CSharp -{ - public readonly struct ReferenceOrConstant - { - private readonly Constant? _constant; - private readonly Reference? _reference; - - private ReferenceOrConstant(Constant constant) - { - Type = constant.Type; - _constant = constant; - _reference = null; - } - - private ReferenceOrConstant(Reference reference) - { - Type = reference.Type; - _reference = reference; - _constant = null; - } - - public CSharpType Type { get; } - public bool IsConstant => _constant.HasValue; - - public Constant Constant => _constant ?? throw new InvalidOperationException("Not a constant"); - public Reference Reference => _reference ?? throw new InvalidOperationException("Not a reference"); - - public static implicit operator ReferenceOrConstant(Constant constant) => new ReferenceOrConstant(constant); - public static implicit operator ReferenceOrConstant(Reference reference) => new ReferenceOrConstant(reference); - public static implicit operator ReferenceOrConstant(Parameter parameter) => new ReferenceOrConstant(new Reference(parameter.Name, parameter.Type)); - public static implicit operator ReferenceOrConstant(FieldDeclaration field) => new ReferenceOrConstant(new Reference(field.Name, field.Type)); - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Types/ModelTypeProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Types/ModelTypeProvider.cs index 5a103a4c12..a1675e6f69 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Types/ModelTypeProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Models/Types/ModelTypeProvider.cs @@ -4,6 +4,7 @@ using System; using Microsoft.Generator.CSharp.Expressions; using Microsoft.Generator.CSharp.Input; +using static Microsoft.Generator.CSharp.Expressions.Snippets; namespace Microsoft.Generator.CSharp { @@ -99,23 +100,20 @@ private bool PropertyHasSetter(CSharpType type, InputModelProperty prop) return true; } - private ConstantExpression? GetPropertyInitializationValue(InputModelProperty property, CSharpType propertyType) + private ValueExpression? GetPropertyInitializationValue(InputModelProperty property, CSharpType propertyType) { if (!property.IsRequired) return null; - // The IsLiteral is returning false for int and float enum value types - https://github.com/Azure/autorest.csharp/issues/4630 - // if (propertyType.IsLiteral && propertyType.Literal?.Value != null) - if (property.Type is InputLiteralType literal) + if (propertyType.IsLiteral) { if (!propertyType.IsNullable) { - var constant = Constant.Parse(literal.Value, propertyType); - return new ConstantExpression(constant); + return Literal(propertyType.Literal); } else { - return new ConstantExpression(Constant.NewInstanceOf(propertyType)); + return DefaultOf(propertyType); } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/Program.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/Program.cs index 0447fc9b63..762edcc087 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/Program.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/Program.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -42,7 +42,7 @@ private static async Task Run(CommandLineOptions options, GeneratorRunner r } catch (Exception ex) { - Console.Error.WriteLine($"Error: {ex.Message}"); + Console.Error.WriteLine($"Error: {ex.Message}: {ex.StackTrace}"); return 1; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Utilities/FormattableStringHelpers.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Utilities/FormattableStringHelpers.cs index 98c8741a8c..e0d34a262f 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Utilities/FormattableStringHelpers.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Utilities/FormattableStringHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System.Security.AccessControl; @@ -94,80 +94,5 @@ public static string ReplaceLast(this string text, string oldValue, string newVa var position = text.LastIndexOf(oldValue, StringComparison.Ordinal); return position < 0 ? text : text.Substring(0, position) + newValue + text.Substring(position + oldValue.Length); } - - public static FormattableString GetReferenceFormattable(this Reference reference) - { - var parts = reference.Name.Split(".").ToArray(); - return Join(parts, parts.Length, static s => s, ".", null, 'I'); - } - - public static FormattableString GetReferenceOrConstantFormattable(this ReferenceOrConstant value) - => value.IsConstant ? value.Constant.GetConstantFormattable() : value.Reference.GetReferenceFormattable(); - - /// - /// This method parses the into a . - /// - /// The to parse. - /// Flag used to determine if the constant should be written as a string. - /// The representing the . - internal static FormattableString GetConstantFormattable(this Constant constant, bool writeAsString = false) - { - if (constant.Value == null) - { - // Cast helps the overload resolution - return $"({constant.Type}){null:L}"; - } - - if (constant.IsNewInstanceSentinel) - { - return $"new {constant.Type}()"; - } - - if (constant.Value is Constant.Expression expression) - { - return expression.ExpressionValue; - } - // TO-DO: Implement once enum types are implemented : https://github.com/Azure/autorest.csharp/issues/4198 - //if (constant is { Type: { IsFrameworkType: false }, Value: EnumTypeValue enumTypeValue }) - //{ - // return $"{constant.Type}.{enumTypeValue.Declaration.Name}"; - //} - - - if (constant.Type is { IsFrameworkType: false, Implementation: EnumType enumType }) - { - if (enumType.IsStringValueType) - return $"new {constant.Type}({constant.Value:L})"; - else - return $"new {constant.Type}(({enumType.ValueType}){constant.Value})"; - } - - Type frameworkType = constant.Type.FrameworkType; - if (frameworkType == typeof(DateTimeOffset)) - { - var d = (DateTimeOffset)constant.Value; - d = d.ToUniversalTime(); - return $"new {typeof(DateTimeOffset)}({d.Year:L}, {d.Month:L}, {d.Day:L} ,{d.Hour:L}, {d.Minute:L}, {d.Second:L}, {d.Millisecond:L}, {typeof(TimeSpan)}.{nameof(TimeSpan.Zero)})"; - } - - if (frameworkType == typeof(byte[])) - { - var bytes = (byte[])constant.Value; - var joinedBytes = string.Join(", ", bytes); - return $"new byte[] {{{joinedBytes}}}"; - } - - if (frameworkType == typeof(ResourceType)) - { - return $"{((ResourceType)constant.Value).ToString():L}"; - } - - if (frameworkType == typeof(bool) && writeAsString) - { - return $"\"{constant.Value!.ToString()!.ToLower()}\""; - } - - return $"{constant.Value:L}"; - } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Writers/CodeWriter.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Writers/CodeWriter.cs index 15a1c995b6..615f29c182 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Writers/CodeWriter.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Writers/CodeWriter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -392,9 +392,6 @@ public CodeWriter AppendRawIf(string str, bool condition) return this; } - public CodeWriter WriteReferenceOrConstant(ReferenceOrConstant value) - => Append(value.GetReferenceOrConstantFormattable()); - public void WriteParameter(Parameter clientParameter) { if (clientParameter.Attributes.Any()) @@ -414,15 +411,8 @@ public void WriteParameter(Parameter clientParameter) Append($"{clientParameter.Type} {clientParameter.Name:D}"); if (clientParameter.DefaultValue != null) { - var defaultValue = clientParameter.DefaultValue.Value; - if (defaultValue.IsNewInstanceSentinel && defaultValue.Type.IsValueType || clientParameter.IsApiVersionParameter && clientParameter.Initializer != null) - { - Append($" = default"); - } - else - { - Append($" = {clientParameter.DefaultValue.Value.GetConstantFormattable()}"); - } + AppendRaw(" = "); + clientParameter.DefaultValue.Write(this); } AppendRaw(","); @@ -883,6 +873,7 @@ public CodeWriter WriteLiteral(object? o) decimal d => SyntaxFactory.Literal(d).ToString(), double d => SyntaxFactory.Literal(d).ToString(), float f => SyntaxFactory.Literal(f).ToString(), + char c => SyntaxFactory.Literal(c).ToString(), bool b => b ? "true" : "false", BinaryData bd => bd.ToArray().Length == 0 ? "new byte[] { }" : SyntaxFactory.Literal(bd.ToString()).ToString(), _ => throw new NotImplementedException()