diff --git a/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderGenericTest.cs b/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderGenericTest.cs index 695f8644..a2f74080 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderGenericTest.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderGenericTest.cs @@ -35,7 +35,7 @@ public void BeforeAllTests() protected override MethodInfo GetClientInstanceMethod(string name) { - return base.GetClientInstanceMethod("ServiceModel.Grpc.TestApi.Domain.IGenericContract." + name); + return base.GetClientInstanceMethod("global::ServiceModel.Grpc.TestApi.Domain.IGenericContract." + name); } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderTest.cs b/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderTest.cs index ccef8df6..07a65fa3 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderTest.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Generator.Test/CSharpClientBuilderTest.cs @@ -37,12 +37,12 @@ public void BeforeAllTests() protected override MethodInfo GetClientInstanceMethod(string name) { - return base.GetClientInstanceMethod(typeof(IContract).FullName + "." + name); + return base.GetClientInstanceMethod("global::" + typeof(IContract).FullName + "." + name); } protected override MethodInfo GetClientInstanceMethod(string name, params Type[] parameters) { - return base.GetClientInstanceMethod(typeof(IContract).FullName + "." + name, parameters); + return base.GetClientInstanceMethod("global::" + typeof(IContract).FullName + "." + name, parameters); } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Roslyn3/ServiceModelGrpcSourceGenerator.cs b/Sources/ServiceModel.Grpc.DesignTime.Roslyn3/ServiceModelGrpcSourceGenerator.cs index 34bdc208..12a5a5f8 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Roslyn3/ServiceModelGrpcSourceGenerator.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Roslyn3/ServiceModelGrpcSourceGenerator.cs @@ -15,7 +15,9 @@ // using System; +using System.Threading; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; namespace ServiceModel.Grpc.DesignTime.Generator { @@ -46,11 +48,25 @@ public void Execute(GeneratorExecutionContext context) var outputContext = new GeneratorContext( context.Compilation, - context.ReportDiagnostic, - context.CancellationToken, - context.AddSource); + new ExecutionContext(context)); new CSharpSourceGenerator().Execute(outputContext, candidates); } } + + private sealed class ExecutionContext : IExecutionContext + { + private readonly GeneratorExecutionContext _context; + + public ExecutionContext(GeneratorExecutionContext context) + { + _context = context; + } + + public CancellationToken CancellationToken => _context.CancellationToken; + + public void ReportDiagnostic(Diagnostic diagnostic) => _context.ReportDiagnostic(diagnostic); + + public void AddSource(string hintName, SourceText sourceText) => _context.AddSource(hintName, sourceText); + } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Roslyn4/ServiceModelGrpcSourceGenerator.cs b/Sources/ServiceModel.Grpc.DesignTime.Roslyn4/ServiceModelGrpcSourceGenerator.cs index 7893b491..4d39a6d8 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Roslyn4/ServiceModelGrpcSourceGenerator.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Roslyn4/ServiceModelGrpcSourceGenerator.cs @@ -15,8 +15,10 @@ // using System.Collections.Immutable; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace ServiceModel.Grpc.DesignTime.Generator { @@ -54,12 +56,26 @@ private static void Execute( var outputContext = new GeneratorContext( source.Compilation, - context.ReportDiagnostic, - context.CancellationToken, - context.AddSource); + new ExecutionContext(context)); new CSharpSourceGenerator().Execute(outputContext, source.Candidates); } } } + + private sealed class ExecutionContext : IExecutionContext + { + private readonly SourceProductionContext _context; + + public ExecutionContext(SourceProductionContext context) + { + _context = context; + } + + public CancellationToken CancellationToken => _context.CancellationToken; + + public void ReportDiagnostic(Diagnostic diagnostic) => _context.ReportDiagnostic(diagnostic); + + public void AddSource(string hintName, SourceText sourceText) => _context.AddSource(hintName, sourceText); + } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSharedCodeGeneratorFactory.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSharedCodeGeneratorFactory.cs index 7ed9044b..759cd9e8 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSharedCodeGeneratorFactory.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSharedCodeGeneratorFactory.cs @@ -63,11 +63,9 @@ private static IEnumerable GetNonBuildInMessages(ContractDescription contra private static void AddPropertiesCount(SortedSet target, MessageDescription? message) { - // see ServiceModel.Grpc.Channel.Message.tt - var length = message?.Properties.Length ?? 0; - if (length > 3) + if (message != null && !message.IsBuiltIn) { - target.Add(length); + target.Add(message.Properties.Length); } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSourceGenerator.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSourceGenerator.cs index 07baacb7..88606786 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSourceGenerator.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/CSharpSourceGenerator.cs @@ -17,12 +17,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using ServiceModel.Grpc.Channel; -using ServiceModel.Grpc.Configuration; namespace ServiceModel.Grpc.DesignTime.Generator { @@ -54,26 +50,11 @@ public void Execute(GeneratorContext context, IEnumerable CreateDefaultUsing() - { - return new HashSet(StringComparer.Ordinal) - { - typeof(Func<>).Namespace, - typeof(IEnumerable<>).Namespace, - typeof(CancellationToken).Namespace, - typeof(Task).Namespace, - "Grpc.Core", - typeof(IMarshallerFactory).Namespace, - typeof(Message).Namespace - }; - } - private void InvokeGenerator(GeneratorContext context, ICodeGeneratorFactory factory, ClassDeclarationSyntax node) { var generatedCount = 0; CompilationUnit unit = default; - ICollection imports = null!; foreach (var generator in factory.GetGenerators()) { @@ -84,14 +65,12 @@ private void InvokeGenerator(GeneratorContext context, ICodeGeneratorFactory fac if (generatedCount == 0) { unit = new CompilationUnit(node); - imports = CreateDefaultUsing(); } else { unit.Output.AppendLine(); } - generator.AddUsing(imports); generator.GenerateMemberDeclaration(unit.Output); generatedCount++; @@ -101,7 +80,7 @@ private void InvokeGenerator(GeneratorContext context, ICodeGeneratorFactory fac context.CancellationToken.ThrowIfCancellationRequested(); if (generatedCount > 0) { - var source = unit.GetSourceText(imports); + var source = unit.GetSourceText(); context.AddOutput(node, factory.GetHintName(), source); } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/CodeGeneratorFactoryResolver.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/CodeGeneratorFactoryResolver.cs index ed8345a8..473bf99a 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/CodeGeneratorFactoryResolver.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/CodeGeneratorFactoryResolver.cs @@ -50,7 +50,7 @@ public bool TryResolve( return false; } - var fullName = SyntaxTools.GetFullName(attributeData.AttributeClass); + var fullName = attributeData.AttributeClass.ToDisplayString(NullableFlowState.None); var isImport = false; if ("ServiceModel.Grpc.DesignTime.ImportGrpcServiceAttribute".Equals(fullName, StringComparison.Ordinal)) { diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/CompilationUnit.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/CompilationUnit.cs index 76682023..4ab12e92 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/CompilationUnit.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/CompilationUnit.cs @@ -28,35 +28,32 @@ namespace ServiceModel.Grpc.DesignTime.Generator { internal readonly ref struct CompilationUnit { - private readonly IList _indentation; + private readonly IDisposable[] _indentation; public CompilationUnit(ClassDeclarationSyntax node) { Output = new CodeStringBuilder(); - _indentation = new List(); - DeclareClass(node); + AddComment(Output); + AddUsing(Output); + _indentation = DeclareClass(node, Output); } public CodeStringBuilder Output { get; } - public string GetSourceText(IEnumerable imports) + public string GetSourceText() { - for (var i = 0; i < _indentation.Count; i++) + for (var i = 0; i < _indentation.Length; i++) { _indentation[i].Dispose(); Output.AppendLine("}"); } var text = Output.AsStringBuilder(); - - InsertImports(text, imports); - InsertComment(text); - return text.ToString(); } - private static void InsertComment(StringBuilder text) + private static void AddComment(CodeStringBuilder output) { var comment = @"// ------------------------------------------------------------------------------ // @@ -71,7 +68,16 @@ private static void InsertComment(StringBuilder text) .Replace("\r\n", "\n") .Replace("\n", Environment.NewLine); - text.Insert(0, comment); + output.Append(comment); + } + + private static void AddUsing(CodeStringBuilder output) + { + output.AppendLine("using System;"); + output.AppendLine("using System.Collections.Generic;"); + output.AppendLine("using System.Threading;"); + output.AppendLine("using System.Threading.Tasks;"); + output.AppendLine(); } private static void InsertImports(StringBuilder text, IEnumerable imports) @@ -84,7 +90,7 @@ private static void InsertImports(StringBuilder text, IEnumerable import } } - private void DeclareClass(ClassDeclarationSyntax node) + private static IDisposable[] DeclareClass(ClassDeclarationSyntax node, CodeStringBuilder output) { var owners = ImmutableArray.Empty; @@ -102,14 +108,17 @@ private void DeclareClass(ClassDeclarationSyntax node) owners = owners.Add("partial class " + node.Identifier.WithoutTrivia()); + var result = new IDisposable[owners.Length]; for (var i = 0; i < owners.Length; i++) { - Output + output .AppendLine(owners[i]) .AppendLine("{"); - _indentation.Add(Output.Indent()); + result[i] = output.Indent(); } + + return result; } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/GeneratorContext.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/GeneratorContext.cs index 34325b68..32743eae 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/GeneratorContext.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/GeneratorContext.cs @@ -27,34 +27,29 @@ namespace ServiceModel.Grpc.DesignTime.Generator { internal sealed class GeneratorContext { - private readonly Action _reportDiagnostic; - private readonly Action _addSource; + private readonly IExecutionContext _executionContext; private readonly HashSet _generatedNames; public GeneratorContext( Compilation compilation, - Action reportDiagnostic, - CancellationToken cancellationToken, - Action addSource) + IExecutionContext executionContext) { + _executionContext = executionContext; Compilation = compilation; - CancellationToken = cancellationToken; - _reportDiagnostic = reportDiagnostic; - _addSource = addSource; _generatedNames = new HashSet(StringComparer.OrdinalIgnoreCase); } - public CancellationToken CancellationToken { get; } + public CancellationToken CancellationToken => _executionContext.CancellationToken; public Compilation Compilation { get; } - public void ReportDiagnostic(Diagnostic diagnostic) => _reportDiagnostic(diagnostic); + public void ReportDiagnostic(Diagnostic diagnostic) => _executionContext.ReportDiagnostic(diagnostic); public void AddOutput(ClassDeclarationSyntax node, string hintName, string source) { var fileName = GetOutputFileName(node, hintName); var sourceText = SourceText.From(source, Encoding.UTF8); - _addSource(fileName, sourceText); + _executionContext.AddSource(fileName, sourceText); } internal string GetOutputFileName(ClassDeclarationSyntax node, string hintName) diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/IExecutionContext.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/IExecutionContext.cs new file mode 100644 index 00000000..69c5adc8 --- /dev/null +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/IExecutionContext.cs @@ -0,0 +1,31 @@ +// +// Copyright 2020-2021 Max Ieremenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace ServiceModel.Grpc.DesignTime.Generator +{ + internal interface IExecutionContext + { + CancellationToken CancellationToken { get; } + + void ReportDiagnostic(Diagnostic diagnostic); + + void AddSource(string hintName, SourceText sourceText); + } +} \ No newline at end of file diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilder.cs index d5b6141b..0e60af32 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilder.cs @@ -18,7 +18,6 @@ using System.Collections.Generic; using System.Globalization; using Grpc.Core; -using ServiceModel.Grpc.Channel; using ServiceModel.Grpc.Client.Internal; namespace ServiceModel.Grpc.DesignTime.Generator.Internal.CSharp @@ -43,7 +42,8 @@ protected override void Generate() WriteMetadata(); Output .Append($"internal sealed class {_contract.ClientClassName} : ") - .AppendFormat("ClientBase<{0}>, ", _contract.ClientClassName) + .AppendType(typeof(ClientBase<>)) + .AppendFormat("{0}>, ", _contract.ClientClassName) .AppendLine(_contract.ContractInterfaceName); Output.AppendLine("{"); @@ -105,9 +105,10 @@ private void BuildCtorCallInvoker() .Append("public ") .Append(_contract.ClientClassName) .Append("(") - .Append(nameof(CallInvoker)).Append(" callInvoker, ") - .Append(_contract.ContractClassName).Append(" contract, ") - .Append("Func defaultCallOptionsFactory") + .AppendType(typeof(CallInvoker)).Append(" callInvoker, ") + .Append(_contract.ContractClassName).Append(" contract, Func<") + .AppendType(typeof(CallOptions)) + .Append("> defaultCallOptionsFactory") .AppendLine(")"); using (Output.Indent()) @@ -118,7 +119,7 @@ private void BuildCtorCallInvoker() Output.AppendLine("{"); using (Output.Indent()) { - Output.AppendLine("if (contract == null) throw new ArgumentNullException(\"contract\");"); + Output.AppendArgumentNullException("contract"); Output.AppendLine("Contract = contract;"); Output.AppendLine("DefaultCallOptionsFactory = defaultCallOptionsFactory;"); @@ -134,8 +135,9 @@ private void BuildCtorConfiguration() .Append(_contract.ClientClassName) .Append("(") .Append("ClientBaseConfiguration configuration, ") - .Append(_contract.ContractClassName).Append(" contract, ") - .Append("Func defaultCallOptionsFactory") + .Append(_contract.ContractClassName).Append(" contract, Func<") + .AppendType(typeof(CallOptions)) + .Append("> defaultCallOptionsFactory") .AppendLine(")"); using (Output.Indent()) @@ -181,7 +183,9 @@ private void BuildProperties() .AppendLine(); Output - .AppendLine("public Func DefaultCallOptionsFactory { get; }") + .Append("public Func<") + .AppendType(typeof(CallOptions)) + .AppendLine("> DefaultCallOptionsFactory { get; }") .AppendLine(); } @@ -226,10 +230,11 @@ private void BuildUnary(OperationDescription operation, string? grpcMethodName) // var __response = new UnaryCall(method, CallInvoker, __callOptionsBuilder) Output .Append(hasReturn ? "var __response = " : string.Empty) - .Append("new UnaryCall<") - .Append(operation.RequestType.ClassName) + .Append("new ") + .AppendType(typeof(UnaryCall<,>)) + .AppendMessage(operation.RequestType) .Append(", ") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .Append(">(Contract.") .Append(grpcMethodName ?? operation.GrpcMethodName) .Append(", CallInvoker, ") @@ -282,12 +287,13 @@ private void BuildClientStreaming(OperationDescription operation) // var __response = new ClientStreamingCall(method, CallInvoker, __callOptionsBuilder) Output - .Append("var __response = new ClientStreamingCall<") - .Append(operation.HeaderRequestType?.ClassName ?? nameof(Message)) + .Append("var __response = new ") + .AppendType(typeof(ClientStreamingCall<,,>)) + .AppendMessageOrDefault(operation.HeaderRequestType) .Append(", ") .Append(operation.RequestType.Properties[0]) .Append(", ") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .Append(">(Contract.") .Append(operation.GrpcMethodName) .Append(", CallInvoker, ") @@ -341,10 +347,11 @@ private void BuildClientStreaming(OperationDescription operation) // var __response = new ServerStreamingCall(method, CallInvoker, __callOptionsBuilder) Output - .Append("var __response = new ServerStreamingCall<") - .Append(operation.RequestType.ClassName) + .Append("var __response = new ") + .AppendType(typeof(ServerStreamingCall<,,>)) + .AppendMessage(operation.RequestType) .Append(", ") - .Append(operation.HeaderResponseType?.ClassName ?? nameof(Message)) + .AppendMessageOrDefault(operation.HeaderResponseType) .Append(", ") .Append(operation.ResponseType.Properties[0]) .Append(">(Contract.") @@ -411,7 +418,7 @@ private void BuildServerStreamingResultAdapter(OperationDescription operation, s .Append(" ") .Append(functionName) .Append("(") - .Append(operation.HeaderResponseType!.ClassName) + .AppendMessage(operation.HeaderResponseType!) .Append(" header, IAsyncEnumerable<") .Append(operation.ResponseType.Properties[0]) .Append(">") @@ -460,12 +467,13 @@ private void BuildServerStreamingResultAdapter(OperationDescription operation, s // var __response = new DuplexStreamingCall(method, CallInvoker, __callOptionsBuilder) Output - .Append("var __response = new DuplexStreamingCall<") - .Append(operation.HeaderRequestType?.ClassName ?? nameof(Message)) + .Append("var __response = new ") + .AppendType(typeof(DuplexStreamingCall<,,,>)) + .AppendMessageOrDefault(operation.HeaderRequestType) .Append(", ") .Append(operation.RequestType.Properties[0]) .Append(", ") - .Append(operation.HeaderResponseType?.ClassName ?? nameof(Message)) + .AppendMessageOrDefault(operation.HeaderResponseType) .Append(", ") .Append(operation.ResponseType.Properties[0]) .Append(">(Contract.") @@ -599,7 +607,7 @@ private void InitializeCallOptionsBuilderVariable(OperationDescription operation .Append("var ") .Append(VarCallOptionsBuilder) .Append(" = new ") - .Append(nameof(CallOptionsBuilder)) + .AppendType(typeof(CallOptionsBuilder)) .Append("(DefaultCallOptionsFactory)"); using (Output.Indent()) @@ -607,8 +615,10 @@ private void InitializeCallOptionsBuilderVariable(OperationDescription operation for (var i = 0; i < operation.ContextInput.Length; i++) { var parameter = operation.Method.Parameters[operation.ContextInput[i]]; - Output.AppendLine(); - Output.AppendFormat(".With{0}({1})", parameter.GetNonNullableType(), parameter.Name); + var type = SyntaxTools.IsNullable(parameter.TypeSymbol) ? parameter.TypeSymbol.GenericTypeArguments()[0] : parameter.TypeSymbol; + Output + .AppendLine() + .Append(".With").Append(type.Name).Append("(").Append(parameter.Name).Append(")"); } Output.AppendLine(";"); @@ -619,7 +629,7 @@ private void CreateRequestMessage(OperationDescription operation) { Output .Append("new ") - .Append(operation.RequestType.ClassName) + .AppendMessage(operation.RequestType) .Append("("); for (var i = 0; i < operation.RequestTypeInput.Length; i++) @@ -639,7 +649,7 @@ private void WithRequestHeader(OperationDescription operation) .Append(".WithRequestHeader(Contract.") .Append(operation.GrpcMethodInputHeaderName) .Append(", new ") - .Append(operation.HeaderRequestType!.ClassName) + .AppendMessage(operation.HeaderRequestType!) .Append("("); for (var i = 0; i < operation.HeaderRequestTypeInput.Length; i++) diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilderBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilderBuilder.cs index b7704f61..f41ef0e8 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilderBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientBuilderBuilder.cs @@ -15,8 +15,10 @@ // using System.Collections.Generic; +using Grpc.Core; using ServiceModel.Grpc.Client; using ServiceModel.Grpc.Client.Internal; +using ServiceModel.Grpc.Configuration; namespace ServiceModel.Grpc.DesignTime.Generator.Internal.CSharp { @@ -31,20 +33,14 @@ public CSharpClientBuilderBuilder(ContractDescription contract) public override string GetGeneratedMemberName() => _contract.ClientBuilderClassName; - public override void AddUsing(ICollection imports) - { - base.AddUsing(imports); - imports.Add(typeof(IClientFactory).Namespace!); - imports.Add(typeof(CallOptionsBuilder).Namespace!); - } - protected override void Generate() { WriteMetadata(); Output .Append("public sealed class ") .Append(_contract.ClientBuilderClassName) - .Append(" : IClientBuilder<") + .Append(" : ") + .AppendType(typeof(IClientBuilder<>)) .Append(_contract.ContractInterfaceName) .AppendLine(">"); Output.AppendLine("{"); @@ -84,19 +80,25 @@ private void BuildFields() .AppendLine(" _contract;"); Output - .AppendLine("private Func _defaultCallOptionsFactory;"); + .AppendLine("private Func<") + .AppendType(typeof(CallOptions)) + .AppendLine("> _defaultCallOptionsFactory;"); } private void BuildMethodInitialize() { Output - .AppendLine("public void Initialize(IMarshallerFactory marshallerFactory, Func defaultCallOptionsFactory)"); + .Append("public void Initialize(") + .AppendType(typeof(IMarshallerFactory)) + .Append(" marshallerFactory, Func<") + .AppendType(typeof(CallOptions)) + .AppendLine("> defaultCallOptionsFactory)"); Output.AppendLine("{"); using (Output.Indent()) { Output - .AppendLine("if (marshallerFactory == null) throw new ArgumentNullException(\"marshallerFactory\");"); + .AppendArgumentNullException("marshallerFactory"); Output .Append("_contract = new ") @@ -115,13 +117,15 @@ private void BuildMethodBuild() Output .Append("public ") .Append(_contract.ContractInterfaceName) - .AppendLine(" Build(CallInvoker callInvoker)"); + .Append(" Build(") + .AppendType(typeof(CallInvoker)) + .AppendLine(" callInvoker)"); Output.AppendLine("{"); using (Output.Indent()) { Output - .AppendLine("if (callInvoker == null) throw new ArgumentNullException(\"callInvoker\");"); + .AppendArgumentNullException("callInvoker"); Output .Append("return new ") diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientFactoryExtensionBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientFactoryExtensionBuilder.cs index 40283526..06934da4 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientFactoryExtensionBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpClientFactoryExtensionBuilder.cs @@ -35,7 +35,9 @@ protected override void Generate() { WriteMetadata(); Output - .Append("public static IClientFactory ") + .Append("public static ") + .AppendType(typeof(IClientFactory)) + .Append(" ") .Append(GetGeneratedMemberName()) .Append("("); @@ -45,12 +47,15 @@ protected override void Generate() } Output - .AppendLine("IClientFactory clientFactory, Action configure = null)") + .AppendType(typeof(IClientFactory)) + .Append(" clientFactory, Action<") + .AppendType(typeof(ServiceModelGrpcClientOptions)) + .AppendLine("> configure = null)") .AppendLine("{"); using (Output.Indent()) { - Output.AppendLine("if (clientFactory == null) throw new ArgumentNullException(\"clientFactory\");"); + Output.AppendArgumentNullException("clientFactory"); Output .Append("clientFactory.") diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpContractBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpContractBuilder.cs index b05b4b1f..8756f3bd 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpContractBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpContractBuilder.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Linq; +using Grpc.Core; using ServiceModel.Grpc.Configuration; namespace ServiceModel.Grpc.DesignTime.Generator.Internal.CSharp @@ -54,14 +55,14 @@ private void BuildCtor() .Append("public ") .Append(_contract.ContractClassName) .Append("(") - .Append(nameof(IMarshallerFactory)) + .AppendType(typeof(IMarshallerFactory)) .AppendLine(" marshallerFactory)"); Output.AppendLine("{"); using (Output.Indent()) { - Output.AppendLine("if (marshallerFactory == null) throw new ArgumentNullException(\"marshallerFactory\");"); + Output.AppendArgumentNullException("marshallerFactory"); foreach (var operation in GetAllOperations()) { @@ -79,10 +80,11 @@ private void BuildProperties() foreach (var operation in GetAllOperations()) { Output - .Append("public Method<") - .Append(operation.RequestType.ClassName) + .Append("public ") + .AppendType(typeof(Method<,>)) + .AppendMessage(operation.RequestType) .Append(", ") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .Append("> ") .Append(operation.GrpcMethodName) .AppendLine(" { get; }") @@ -91,8 +93,9 @@ private void BuildProperties() if (operation.HeaderRequestType != null) { Output - .Append("public Marshaller<") - .Append(operation.HeaderRequestType.ClassName) + .Append("public ") + .AppendType(typeof(Marshaller<>)) + .AppendMessage(operation.HeaderRequestType) .Append("> ") .Append(operation.GrpcMethodInputHeaderName) .AppendLine(" { get; }") @@ -102,8 +105,9 @@ private void BuildProperties() if (operation.HeaderResponseType != null) { Output - .Append("public Marshaller<") - .Append(operation.HeaderResponseType.ClassName) + .Append("public ") + .AppendType(typeof(Marshaller<>)) + .AppendMessage(operation.HeaderResponseType) .Append("> ") .Append(operation.GrpcMethodOutputHeaderName) .AppendLine(" { get; }") @@ -116,14 +120,16 @@ private void BuildMethodInitializer(OperationDescription operation) { Output .Append(operation.GrpcMethodName) - .Append(" = new Method<") - .Append(operation.RequestType.ClassName) + .Append(" = new ") + .AppendType(typeof(Method<,>)) + .AppendMessage(operation.RequestType) .Append(", ") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .Append(">("); Output - .Append("MethodType.") + .AppendType(typeof(MethodType)) + .Append(".") .Append(operation.OperationType.ToString()) .Append(","); @@ -139,12 +145,12 @@ private void BuildMethodInitializer(OperationDescription operation) Output .Append(" marshallerFactory.CreateMarshaller<") - .Append(operation.RequestType.ClassName) + .AppendMessage(operation.RequestType) .Append(">(),"); Output .Append(" marshallerFactory.CreateMarshaller<") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .AppendLine(">());"); } @@ -158,7 +164,7 @@ private void BuildRequestHeaderInitializer(OperationDescription operation) Output .Append(operation.GrpcMethodInputHeaderName) .Append(" = marshallerFactory.CreateMarshaller<") - .Append(operation.HeaderRequestType.ClassName) + .AppendMessage(operation.HeaderRequestType) .AppendLine(">();"); } @@ -172,7 +178,7 @@ private void BuildResponseHeaderInitializer(OperationDescription operation) Output .Append(operation.GrpcMethodOutputHeaderName) .Append(" = marshallerFactory.CreateMarshaller<") - .Append(operation.HeaderResponseType.ClassName) + .AppendMessage(operation.HeaderResponseType) .AppendLine(">();"); } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpMessageBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpMessageBuilder.cs index 3ca0bc41..eb69a9a0 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpMessageBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpMessageBuilder.cs @@ -14,7 +14,7 @@ // limitations under the License. // -using System.Collections.Generic; +using System; using System.Globalization; using System.Runtime.Serialization; using ServiceModel.Grpc.Channel; @@ -32,18 +32,12 @@ public CSharpMessageBuilder(int propertiesCount) public override string GetGeneratedMemberName() => "Message_" + _propertiesCount.ToString(CultureInfo.InvariantCulture); - public override void AddUsing(ICollection imports) - { - base.AddUsing(imports); - imports.Add(typeof(DataContractAttribute).Namespace); - } - protected override void Generate() { WriteMetadata(); Output - .AppendLine("[Serializable]") - .AppendLine("[DataContract(Name = \"m\", Namespace = \"s\")]") + .AppendAttribute(typeof(SerializableAttribute)) + .AppendAttribute(typeof(DataContractAttribute), "Name = \"m\"", "Namespace = \"s\"") .Append("internal sealed class ") .Append(nameof(Message)) .Append("<"); @@ -115,11 +109,12 @@ private void BuildProperties() { for (var i = 0; i < _propertiesCount; i++) { + var order = (i + 1).ToString(CultureInfo.InvariantCulture); Output .AppendLine() - .AppendFormat("[DataMember(Name = \"v{0}\", Order = {0})]", i + 1) + .AppendAttribute(typeof(DataMemberAttribute), string.Format("Name = \"v{0}\"", order), string.Format("Order = {0}", order)) .AppendLine() - .AppendFormat("public T{0} Value{0}", i + 1) + .AppendFormat("public T{0} Value{0}", order) .AppendLine(" { get; set; }"); } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetAddOptionsBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetAddOptionsBuilder.cs index c0e4a10a..1214e9fd 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetAddOptionsBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetAddOptionsBuilder.cs @@ -31,17 +31,13 @@ public CSharpServiceAspNetAddOptionsBuilder(ContractDescription contract, bool i public override string GetGeneratedMemberName() => "Add" + _contract.BaseClassName + "Options"; - public override void AddUsing(ICollection imports) - { - base.AddUsing(imports); - imports.Add("Microsoft.Extensions.DependencyInjection"); - } - protected override void Generate() { WriteMetadata(); Output - .Append("public static IServiceCollection ") + .Append("public static ") + .AppendTypeName("Microsoft.Extensions.DependencyInjection", "IServiceCollection") + .Append(" ") .Append(GetGeneratedMemberName()) .Append("("); @@ -51,7 +47,10 @@ protected override void Generate() } Output - .Append("IServiceCollection services, Action> configure)") .AppendLine("{"); @@ -59,10 +58,12 @@ protected override void Generate() using (Output.Indent()) { Output - .AppendLine("if (services == null) throw new ArgumentNullException(\"services\");") - .Append("return services.AddServiceModelGrpcServiceOptions<") + .AppendArgumentNullException("services") + .Append("return ") + .AppendTypeName("Microsoft.Extensions.DependencyInjection", "ServiceCollectionExtensions") + .Append(".AddServiceModelGrpcServiceOptions<") .Append(_contract.ContractInterfaceName) - .AppendLine(">(configure);"); + .AppendLine(">(services, configure);"); } Output.AppendLine("}"); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetMapGrpcServiceBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetMapGrpcServiceBuilder.cs index be5f3f3a..dc1675a9 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetMapGrpcServiceBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceAspNetMapGrpcServiceBuilder.cs @@ -31,18 +31,13 @@ public CSharpServiceAspNetMapGrpcServiceBuilder(ContractDescription contract, bo public override string GetGeneratedMemberName() => "Map" + _contract.BaseClassName; - public override void AddUsing(ICollection imports) - { - base.AddUsing(imports); - imports.Add("Microsoft.AspNetCore.Routing"); - imports.Add("Microsoft.AspNetCore.Builder"); - } - protected override void Generate() { WriteMetadata(); Output - .Append("public static GrpcServiceEndpointConventionBuilder ") + .Append("public static ") + .AppendTypeName("Microsoft.AspNetCore.Builder", "GrpcServiceEndpointConventionBuilder") + .Append(" ") .Append(GetGeneratedMemberName()) .Append("("); @@ -52,18 +47,21 @@ protected override void Generate() } Output - .AppendLine("IEndpointRouteBuilder builder)") + .AppendTypeName("Microsoft.AspNetCore.Routing", "IEndpointRouteBuilder") + .AppendLine(" builder)") .AppendLine("{"); using (Output.Indent()) { Output - .AppendLine("if (builder == null) throw new ArgumentNullException(\"builder\");") - .Append("return builder.MapGrpcService<") + .AppendArgumentNullException("builder") + .Append("return ") + .AppendTypeName("Microsoft.AspNetCore.Builder", "ServiceModelGrpcEndpointRouteBuilderExtensions") + .Append(".MapGrpcService<") .Append(_contract.ContractInterfaceName) .Append(", ") .Append(_contract.EndpointBinderClassName) - .AppendLine(">();"); + .AppendLine(">(builder);"); } Output.AppendLine("}"); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBinderBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBinderBuilder.cs index eddc7c42..1f6840f1 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBinderBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBinderBuilder.cs @@ -22,6 +22,7 @@ using Grpc.Core; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using ServiceModel.Grpc.Channel; using ServiceModel.Grpc.Hosting.Internal; namespace ServiceModel.Grpc.DesignTime.Generator.Internal.CSharp @@ -37,15 +38,6 @@ public CSharpServiceEndpointBinderBuilder(ContractDescription contract) public override string GetGeneratedMemberName() => _contract.EndpointBinderClassName; - public override void AddUsing(ICollection imports) - { - base.AddUsing(imports); - imports.Add(typeof(MethodInfo).Namespace!); - imports.Add(typeof(Expression).Namespace!); - imports.Add(typeof(IServiceMethodBinder<>).Namespace!); - imports.Add(typeof(IServiceEndpointBinder<>).Namespace!); - } - internal static void WriteNewAttribute(CodeStringBuilder output, AttributeData attribute) { output @@ -120,8 +112,7 @@ protected override void Generate() .Append("internal sealed partial class ") .Append(GetGeneratedMemberName()) .Append(" : ") - .Append(nameof(IServiceEndpointBinder)) - .Append("<") + .AppendType(typeof(IServiceEndpointBinder<>)) .Append(_contract.ContractInterfaceName) .AppendLine(">"); Output.AppendLine("{"); @@ -157,15 +148,14 @@ private void BuildBind() { Output .Append("public void Bind(") - .Append(nameof(IServiceMethodBinder)) - .Append("<") + .AppendType(typeof(IServiceMethodBinder<>)) .Append(_contract.ContractInterfaceName) .AppendLine("> methodBinder)") .AppendLine("{"); using (Output.Indent()) { - Output.AppendLine("if (methodBinder == null) throw new ArgumentNullException(\"methodBinder\");"); + Output.AppendArgumentNullException("methodBinder"); Output .Append("var contract = new ") @@ -191,47 +181,16 @@ private void BuildBind() if (method.OperationType == MethodType.ClientStreaming) { - var requestHeaderMarshaller = "(Marshaller)null"; - if (method.HeaderRequestType != null) - { - requestHeaderMarshaller = "contract." + method.GrpcMethodInputHeaderName; - } - - Output - .Append(", ") - .Append(requestHeaderMarshaller); + WriteHeaderMarshaller(method.HeaderRequestType, method.GrpcMethodInputHeaderName); } else if (method.OperationType == MethodType.ServerStreaming) { - var responseHeaderMarshaller = "(Marshaller)null"; - if (method.HeaderResponseType != null) - { - responseHeaderMarshaller = "contract." + method.GrpcMethodOutputHeaderName; - } - - Output - .Append(", ") - .Append(responseHeaderMarshaller); + WriteHeaderMarshaller(method.HeaderResponseType, method.GrpcMethodOutputHeaderName); } else if (method.OperationType == MethodType.DuplexStreaming) { - var requestHeaderMarshaller = "(Marshaller)null"; - if (method.HeaderRequestType != null) - { - requestHeaderMarshaller = "contract." + method.GrpcMethodInputHeaderName; - } - - var responseHeaderMarshaller = "(Marshaller)null"; - if (method.HeaderResponseType != null) - { - responseHeaderMarshaller = "contract." + method.GrpcMethodOutputHeaderName; - } - - Output - .Append(", ") - .Append(requestHeaderMarshaller) - .Append(", ") - .Append(responseHeaderMarshaller); + WriteHeaderMarshaller(method.HeaderRequestType, method.GrpcMethodInputHeaderName); + WriteHeaderMarshaller(method.HeaderResponseType, method.GrpcMethodOutputHeaderName); } Output @@ -247,6 +206,26 @@ private void BuildBind() Output.AppendLine("}"); } + private void WriteHeaderMarshaller(MessageDescription? description, string propertyName) + { + Output.Append(", "); + if (description == null) + { + // (Marshaller)null + Output + .Append("(") + .AppendType(typeof(Marshaller<>)) + .AppendType(typeof(Message)) + .Append(">)null"); + } + else + { + Output + .Append("contract.") + .Append(propertyName); + } + } + private void BuildGetServiceMetadata() { Output @@ -356,7 +335,7 @@ private void BuildGetMethodDefinition(string interfaceType, OperationDescription { Output .Append("private ") - .Append(nameof(MethodInfo)) + .AppendType(typeof(MethodInfo)) .Append(" ") .Append(GetMethodDefinitionName(method)) .AppendLine("()") @@ -387,7 +366,7 @@ private void BuildGetMethodDefinition(string interfaceType, OperationDescription // Expression> __exp = s => s.Sum(default, default); Output - .Append(nameof(Expression)) + .AppendType(typeof(Expression)) .Append("> __exp = s => s.") @@ -425,7 +404,10 @@ private void BuildGetMethodDefinition(string interfaceType, OperationDescription Output.AppendLine(");"); - Output.AppendLine("return ((MethodCallExpression)__exp.Body).Method;"); + Output + .Append("return ((") + .AppendType(typeof(MethodCallExpression)) + .AppendLine(")__exp.Body).Method;"); } Output.AppendLine("}"); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBuilder.cs index 0aaf3724..d530733c 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceEndpointBuilder.cs @@ -19,7 +19,6 @@ using System.Linq; using System.Threading; using Grpc.Core; -using ServiceModel.Grpc.Channel; namespace ServiceModel.Grpc.DesignTime.Generator.Internal.CSharp { @@ -92,12 +91,12 @@ private void BuildUnary(string interfaceType, OperationDescription operation) } Output - .Append("Task<").Append(operation.ResponseType.ClassName).Append("> ") + .Append("Task<").AppendMessage(operation.ResponseType).Append("> ") .Append(operation.OperationName) .Append("(") .Append(interfaceType).Append(" service, ") - .Append(operation.RequestType.ClassName).Append(" request, ") - .Append(nameof(ServerCallContext)).AppendLine(" context)") + .AppendMessage(operation.RequestType).Append(" request, ") + .AppendType(typeof(ServerCallContext)).AppendLine(" context)") .AppendLine("{"); using (Output.Indent()) @@ -154,7 +153,7 @@ private void BuildUnary(string interfaceType, OperationDescription operation) Output .Append("new ") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .Append("("); if (operation.ResponseType.Properties.Length > 0) @@ -179,13 +178,13 @@ private void BuildClientStreaming(string interfaceType, OperationDescription ope { // Task Invoke(TService service, TRequestHeader requestHeader, IAsyncEnumerable request, ServerCallContext context) Output - .Append("public async Task<").Append(operation.ResponseType.ClassName).Append("> ") + .Append("public async Task<").AppendMessage(operation.ResponseType).Append("> ") .Append(operation.OperationName) .Append("(") .Append(interfaceType).Append(" service, ") - .Append(operation.HeaderRequestType?.ClassName ?? nameof(Message)).Append(" requestHeader, ") + .AppendMessageOrDefault(operation.HeaderRequestType).Append(" requestHeader, ") .Append("IAsyncEnumerable<").Append(operation.RequestType.Properties[0]).Append(">").Append(" request, ") - .Append(nameof(ServerCallContext)).AppendLine(" context)") + .AppendType(typeof(ServerCallContext)).AppendLine(" context)") .AppendLine("{"); using (Output.Indent()) @@ -230,7 +229,7 @@ private void BuildClientStreaming(string interfaceType, OperationDescription ope Output .Append("new ") - .Append(operation.ResponseType.ClassName) + .AppendMessage(operation.ResponseType) .Append("("); if (operation.ResponseType.Properties.Length > 0) @@ -251,15 +250,15 @@ private void BuildServerStreaming(string interfaceType, OperationDescription ope .Append("public") .Append(operation.IsAsync ? " async" : string.Empty) .Append(" ValueTask<(") - .Append(operation.HeaderResponseType?.ClassName ?? nameof(Message)) + .AppendMessageOrDefault(operation.HeaderResponseType) .Append(", IAsyncEnumerable<") .Append(operation.ResponseType.Properties[0]) .Append(">)> ") .Append(operation.OperationName) .Append("(") .Append(interfaceType).Append(" service, ") - .Append(operation.RequestType.ClassName).Append(" request, ") - .Append(nameof(ServerCallContext)).AppendLine(" context)") + .AppendMessage(operation.RequestType).Append(" request, ") + .AppendType(typeof(ServerCallContext)).AppendLine(" context)") .AppendLine("{"); using (Output.Indent()) @@ -316,16 +315,16 @@ private void BuildDuplexStreaming(string interfaceType, OperationDescription ope .Append("public") .Append(operation.IsAsync ? " async" : string.Empty) .Append(" ValueTask<(") - .Append(operation.HeaderResponseType?.ClassName ?? nameof(Message)) + .AppendMessageOrDefault(operation.HeaderResponseType) .Append(", IAsyncEnumerable<") .Append(operation.ResponseType.Properties[0]) .Append(">)> ") .Append(operation.OperationName) .Append("(") .Append(interfaceType).Append(" service, ") - .Append(operation.HeaderRequestType?.ClassName ?? nameof(Message)).Append(" requestHeader, ") + .AppendMessageOrDefault(operation.HeaderRequestType).Append(" requestHeader, ") .Append("IAsyncEnumerable<").Append(operation.RequestType.Properties[0]).Append("> request, ") - .Append(nameof(ServerCallContext)).AppendLine(" context)") + .AppendType(typeof(ServerCallContext)).AppendLine(" context)") .AppendLine("{"); using (Output.Indent()) @@ -392,7 +391,7 @@ private void BuildWriteServerStreamingResult(OperationDescription operation) { Output .Append("new ValueTask<(") - .Append(operation.HeaderResponseType?.ClassName ?? nameof(Message)) + .AppendMessageOrDefault(operation.HeaderResponseType) .Append(", IAsyncEnumerable<") .Append(operation.ResponseType.Properties[0]) .Append(">)>(("); @@ -406,7 +405,7 @@ private void BuildWriteServerStreamingResult(OperationDescription operation) { Output .Append("new ") - .Append(operation.HeaderResponseType.ClassName) + .AppendMessage(operation.HeaderResponseType) .Append("("); for (var i = 0; i < operation.HeaderResponseTypeInput.Length; i++) @@ -469,7 +468,7 @@ private void PushContext(ParameterDescription parameter) // new CallOptions(context.RequestHeaders, context.Deadline, context.CancellationToken, context.WriteOptions) Output .Append("new ") - .Append(nameof(CallOptions)) + .AppendType(typeof(CallOptions)) .Append("(") .Append("context.").Append(nameof(ServerCallContext.RequestHeaders)) .Append(", context.").Append(nameof(ServerCallContext.Deadline)) diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddProviderServiceBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddProviderServiceBuilder.cs index 5bff9a8d..3bf08cbf 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddProviderServiceBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddProviderServiceBuilder.cs @@ -33,7 +33,9 @@ protected override void Generate() { WriteMetadata(); Output - .Append("public static Server.ServiceDefinitionCollection Add") + .Append("public static ") + .AppendTypeName("Grpc.Core", "Server.ServiceDefinitionCollection") + .Append(" Add") .Append(_contract.BaseClassName) .Append("("); @@ -43,17 +45,19 @@ protected override void Generate() } Output - .Append("Server.ServiceDefinitionCollection services") - .Append(", IServiceProvider serviceProvider") + .AppendTypeName("Grpc.Core", "Server.ServiceDefinitionCollection") + .Append(" services, IServiceProvider serviceProvider") .AppendLine(", Action configure = default)") .AppendLine("{"); using (Output.Indent()) { Output - .Append("return services.AddServiceModel<") + .Append("return ") + .AppendTypeName("Grpc.Core", "ServiceDefinitionCollectionExtensions") + .Append(".AddServiceModel<") .Append(_contract.ContractInterfaceName) - .AppendLine(">(serviceProvider, configure);"); + .AppendLine(">(services, serviceProvider, configure);"); } Output.AppendLine("}"); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddSingletonServiceBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddSingletonServiceBuilder.cs index 39a5c39c..272c7c66 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddSingletonServiceBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddSingletonServiceBuilder.cs @@ -33,7 +33,9 @@ protected override void Generate() { WriteMetadata(); Output - .Append("public static Server.ServiceDefinitionCollection Add") + .Append("public static ") + .AppendTypeName("Grpc.Core", "Server.ServiceDefinitionCollection") + .Append(" Add") .Append(_contract.BaseClassName) .Append("("); @@ -43,7 +45,8 @@ protected override void Generate() } Output - .Append("Server.ServiceDefinitionCollection services, ") + .AppendTypeName("Grpc.Core", "Server.ServiceDefinitionCollection") + .Append(" services, ") .Append(_contract.ContractInterfaceName) .AppendLine(" service, Action configure = default)") .AppendLine("{"); @@ -51,7 +54,9 @@ protected override void Generate() using (Output.Indent()) { Output - .AppendLine("return services.AddServiceModelSingleton(service, configure);"); + .Append("return ") + .AppendTypeName("Grpc.Core", "ServiceDefinitionCollectionExtensions") + .AppendLine(".AddServiceModelSingleton(services, service, configure);"); } Output.AppendLine("}"); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddTransientServiceBuilder.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddTransientServiceBuilder.cs index 10121af7..6b0b24f2 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddTransientServiceBuilder.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CSharpServiceSelfHostAddTransientServiceBuilder.cs @@ -33,7 +33,9 @@ protected override void Generate() { WriteMetadata(); Output - .Append("public static Server.ServiceDefinitionCollection Add") + .Append("public static ") + .AppendTypeName("Grpc.Core", "Server.ServiceDefinitionCollection") + .Append(" Add") .Append(_contract.BaseClassName) .Append("("); @@ -43,7 +45,8 @@ protected override void Generate() } Output - .Append("Server.ServiceDefinitionCollection services, Func<") + .AppendTypeName("Grpc.Core", "Server.ServiceDefinitionCollection") + .Append(" services, Func<") .Append(_contract.ContractInterfaceName) .AppendLine("> serviceFactory, Action configure = default)") .AppendLine("{"); @@ -51,7 +54,9 @@ protected override void Generate() using (Output.Indent()) { Output - .AppendLine("return services.AddServiceModelTransient(serviceFactory, configure);"); + .Append("return ") + .AppendTypeName("Grpc.Core", "ServiceDefinitionCollectionExtensions") + .AppendLine(".AddServiceModelTransient(services, serviceFactory, configure);"); } Output.AppendLine("}"); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeGeneratorBase.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeGeneratorBase.cs index 5f9d7d6d..86b8908a 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeGeneratorBase.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeGeneratorBase.cs @@ -15,7 +15,6 @@ // using System.CodeDom.Compiler; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; @@ -34,25 +33,15 @@ public void GenerateMemberDeclaration(CodeStringBuilder output) public abstract string GetGeneratedMemberName(); - public virtual void AddUsing(ICollection imports) - { - imports.Add(typeof(GeneratedCodeAttribute).Namespace); - imports.Add(typeof(CompilerGeneratedAttribute).Namespace); - imports.Add(typeof(ExcludeFromCodeCoverageAttribute).Namespace); - imports.Add(typeof(ObfuscationAttribute).Namespace); - } - protected abstract void Generate(); protected void WriteMetadata() { Output - .Append("[GeneratedCode(\"ServiceModel.Grpc\", \"") - .Append(GetType().Assembly.GetName().Version.ToString(3)) - .AppendLine("\")]") - .AppendLine("[CompilerGenerated]") - .AppendLine("[ExcludeFromCodeCoverage]") - .AppendLine("[Obfuscation(Exclude = true)]"); + .AppendAttribute(typeof(GeneratedCodeAttribute), "\"ServiceModel.Grpc\"", "\"" + GetType().Assembly.GetName().Version.ToString(3) + "\"") + .AppendAttribute(typeof(CompilerGeneratedAttribute)) + .AppendAttribute(typeof(ExcludeFromCodeCoverageAttribute)) + .AppendAttribute(typeof(ObfuscationAttribute), "Exclude = true"); } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeStringBuilderExtensions.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeStringBuilderExtensions.cs new file mode 100644 index 00000000..272d1fb5 --- /dev/null +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/CSharp/CodeStringBuilderExtensions.cs @@ -0,0 +1,110 @@ +// +// Copyright 2022 Max Ieremenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using ServiceModel.Grpc.Channel; + +namespace ServiceModel.Grpc.DesignTime.Generator.Internal.CSharp +{ + internal static class CodeStringBuilderExtensions + { + public static CodeStringBuilder AppendAttribute(this CodeStringBuilder code, Type attributeType, params string[] args) + { + var name = attributeType.Name; + if (name.EndsWith("Attribute", StringComparison.Ordinal)) + { + name = name.Substring(0, attributeType.Name.Length - 9); + } + + code + .Append("[") + .AppendTypeName(attributeType.Namespace, name); + + if (args.Length > 0) + { + code.Append("("); + for (var i = 0; i < args.Length; i++) + { + code + .AppendCommaIf(i != 0) + .Append(args[i]); + } + + code.Append(")"); + } + + return code.AppendLine("]"); + } + + public static CodeStringBuilder AppendArgumentNullException(this CodeStringBuilder code, string paramName) + { + return code + .Append("if (") + .Append(paramName) + .Append(" == null) throw new ArgumentNullException(\"") + .Append(paramName) + .AppendLine("\");"); + } + + public static CodeStringBuilder AppendTypeName(this CodeStringBuilder code, string? typeNamespace, string name) + { + if (typeNamespace != null) + { + code + .Append("global::") + .Append(typeNamespace) + .Append("."); + } + + var index = name.IndexOf('`'); + if (index > 0) + { + code.Append(name.Substring(0, index)); + code.Append("<"); + } + else + { + code.Append(name); + } + + return code; + } + + public static CodeStringBuilder AppendType(this CodeStringBuilder code, Type type) + { + return AppendTypeName(code, type.Namespace, type.Name); + } + + public static CodeStringBuilder AppendMessage(this CodeStringBuilder code, MessageDescription message) + { + if (message.IsBuiltIn) + { + code + .Append("global::") + .Append(typeof(Message).Namespace) + .Append("."); + } + + code.Append(message.ClassName); + return code; + } + + public static CodeStringBuilder AppendMessageOrDefault(this CodeStringBuilder code, MessageDescription? message) + { + return AppendMessage(code, message ?? MessageDescription.Empty); + } + } +} \ No newline at end of file diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/MessageDescription.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/MessageDescription.cs index 8ed7948f..c0cde4c3 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/MessageDescription.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/MessageDescription.cs @@ -28,11 +28,14 @@ public MessageDescription(string[] properties) ClassName = GetClassName(properties); } + public static MessageDescription Empty { get; } = new MessageDescription(Array.Empty()); + public string ClassName { get; } public string[] Properties { get; } - internal static MessageDescription Empty() => new MessageDescription(Array.Empty()); + // see ServiceModel.Grpc.Channel.Message.tt + public bool IsBuiltIn => Properties.Length < 4; private static string GetClassName(string[] properties) { diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/OperationDescription.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/OperationDescription.cs index 38b6dc6b..763ded15 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/OperationDescription.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/OperationDescription.cs @@ -138,7 +138,7 @@ private static MessageDescription CreateMessage(params ITypeSymbol[] properties) { if (SyntaxTools.IsVoid(returnType)) { - return (MessageDescription.Empty(), 0, null, Array.Empty()); + return (MessageDescription.Empty, 0, null, Array.Empty()); } var responseType = returnType; @@ -147,7 +147,7 @@ private static MessageDescription CreateMessage(params ITypeSymbol[] properties) var genericArguments = responseType.GenericTypeArguments(); if (genericArguments.IsEmpty) { - return (MessageDescription.Empty(), 0, null, Array.Empty()); + return (MessageDescription.Empty, 0, null, Array.Empty()); } responseType = genericArguments[0]; @@ -217,7 +217,7 @@ private static MessageDescription CreateMessage(params ITypeSymbol[] properties) { if (Method.Parameters.Length == 0) { - return (MessageDescription.Empty(), Array.Empty(), null, Array.Empty()); + return (MessageDescription.Empty, Array.Empty(), null, Array.Empty()); } var dataParameters = new List(); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/ParameterDescription.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/ParameterDescription.cs index 491a4f9f..85a1c4dc 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/ParameterDescription.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/ParameterDescription.cs @@ -38,15 +38,5 @@ public ParameterDescription(IParameterSymbol parameter) public bool IsOut { get; } public bool IsRef { get; } - - public string GetNonNullableType() - { - if (!SyntaxTools.IsNullable(TypeSymbol)) - { - return Type; - } - - return SyntaxTools.GetFullName(TypeSymbol.GenericTypeArguments()[0]); - } } } diff --git a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/SyntaxTools.cs b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/SyntaxTools.cs index ce6177f7..30206583 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/SyntaxTools.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Shared/Internal/SyntaxTools.cs @@ -19,7 +19,6 @@ using System.Collections.Immutable; using System.IO; using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using ServiceModel.Grpc.Internal; @@ -55,7 +54,7 @@ public static ICollection ExpandInterface(INamedTypeSymbol typ { foreach (var attribute in owner.GetAttributes()) { - var fullName = GetFullName(attribute.AttributeClass!); + var fullName = attribute.AttributeClass?.ToDisplayString(NullableFlowState.None); if (string.Equals(attributeTypeFullName, fullName, StringComparison.Ordinal)) { return attribute; @@ -76,8 +75,13 @@ public static IEnumerable GetInstanceMethods(INamedTypeSymbol typ } } - public static string GetFullName(ITypeSymbol type) + public static string GetFullName(ITypeSymbol? type) { + if (type == null) + { + return string.Empty; + } + if (type.Kind == SymbolKind.TypeParameter) { return type.Name; @@ -91,31 +95,18 @@ public static string GetFullName(ITypeSymbol type) public static string GetNamespace(ITypeSymbol type) { - var result = new StringBuilder(); - - var test = type; - if (test.ContainingType != null) + if (type.ContainingType != null) { - if (result.Length != 0) - { - result.Insert(0, '.'); - } - - result.Insert(0, test.ContainingType.Name); - test = test.ContainingType; + return type.ContainingType.ToDisplayString(); } - foreach (var ns in ExpandNamespace(test.ContainingNamespace)) + var ns = type.ContainingNamespace; + if (ns == null || ns.IsGlobalNamespace) { - if (result.Length != 0) - { - result.Insert(0, '.'); - } - - result.Insert(0, ns.Name); + return string.Empty; } - return result.ToString(); + return ns.ToDisplayString(); } public static ImmutableArray GenericTypeArguments(this ITypeSymbol type) @@ -221,27 +212,36 @@ public static bool IsAssignableFrom(this ITypeSymbol type, ITypeSymbol expected) return false; } - public static bool IsVoid(ITypeSymbol type) => IsMatch(type, typeof(void)); + public static bool IsVoid(ITypeSymbol type) + { + return type.Name.Equals("Void", StringComparison.Ordinal) + && type.GenericTypeArguments().Length == 0 + && "System".Equals(GetNamespace(type), StringComparison.Ordinal); + } public static bool IsTask(ITypeSymbol type) { - return IsMatch(type, typeof(Task)) - || IsMatch(type, typeof(Task<>)) - || IsValueTask(type); + if (type.GenericTypeArguments().Length > 1) + { + return false; + } + + return ("Task".Equals(type.Name, StringComparison.Ordinal) + || "ValueTask".Equals(type.Name, StringComparison.Ordinal)) + && "System.Threading.Tasks".Equals(GetNamespace(type), StringComparison.Ordinal); } public static bool IsValueTask(this ITypeSymbol type) { - return IsMatch(type, typeof(ValueTask)) - || IsMatch(type, typeof(ValueTask<>)); + return type.GenericTypeArguments().Length < 2 + && "ValueTask".Equals(type.Name, StringComparison.Ordinal) + && "System.Threading.Tasks".Equals(GetNamespace(type), StringComparison.Ordinal); } public static bool IsAsyncEnumerable(ITypeSymbol type) { - return type is INamedTypeSymbol named - && named.IsGenericType - && named.TypeArguments.Length == 1 - && "IAsyncEnumerable".Equals(named.Name, StringComparison.Ordinal) + return type.GenericTypeArguments().Length == 1 + && "IAsyncEnumerable".Equals(type.Name, StringComparison.Ordinal) && "System.Collections.Generic".Equals(GetNamespace(type), StringComparison.Ordinal); } @@ -322,16 +322,11 @@ public static bool IsMatch(ITypeSymbol type, Type expected) && GenericTypeArguments(type).IsEmpty; } - public static bool IsNullable(ITypeSymbol type) => IsMatch(type, typeof(Nullable<>)); - - private static IEnumerable ExpandNamespace(INamespaceSymbol? startFrom) + public static bool IsNullable(ITypeSymbol type) { - var ns = startFrom; - while (ns != null && !ns.IsGlobalNamespace) - { - yield return ns; - ns = ns.ContainingNamespace; - } + return type.Name.Equals(nameof(Nullable), StringComparison.Ordinal) + && type.GenericTypeArguments().Length == 1 + && "System".Equals(GetNamespace(type), StringComparison.Ordinal); } private static void WriteTypeFullName(ITypeSymbol type, StringBuilder result) @@ -371,7 +366,7 @@ private static void WriteTypeFullName(ITypeSymbol type, StringBuilder result) return; } - WriteTypeFullName(GetNamespace(type), type.Name, result); + WriteType(type, result); // System.Tuple`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib if (!genericArguments.IsEmpty) @@ -392,30 +387,30 @@ private static void WriteTypeFullName(ITypeSymbol type, StringBuilder result) } } - private static void WriteTypeFullName(string? ns, string name, StringBuilder result) + private static void WriteType(ITypeSymbol type, StringBuilder result) { + var ns = GetNamespace(type); + if ("System".Equals(ns, StringComparison.Ordinal)) { - result.Append(SimplifyTypeName(name)); + result.Append(SimplifyTypeName(type.Name)); return; } if ("System.Collections.Generic".Equals(ns, StringComparison.Ordinal) || "System.Threading.Tasks".Equals(ns, StringComparison.Ordinal) - || "System.Threading".Equals(ns, StringComparison.Ordinal) - || "ServiceModel.Grpc".Equals(ns, StringComparison.Ordinal) - || "Grpc.Core".Equals(ns, StringComparison.Ordinal)) + || "System.Threading".Equals(ns, StringComparison.Ordinal)) { - result.Append(name); + result.Append(type.Name); return; } if (!string.IsNullOrEmpty(ns)) { - result.Append(ns).Append("."); + result.Append("global::").Append(ns).Append("."); } - result.Append(name); + result.Append(type.Name); } private static string SimplifyTypeName(string name) diff --git a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/GeneratorContextTest.cs b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/GeneratorContextTest.cs index 8466d34e..7b52c92e 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/GeneratorContextTest.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/GeneratorContextTest.cs @@ -14,7 +14,6 @@ // limitations under the License. // -using System.Threading; using Microsoft.CodeAnalysis.CSharp; using NUnit.Framework; using Shouldly; @@ -29,7 +28,7 @@ public class GeneratorContextTest [SetUp] public void BeforeEachTest() { - _sut = new GeneratorContext(null!, null!, CancellationToken.None, null!); + _sut = new GeneratorContext(null!, null!); } [Test] diff --git a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/CSharp/CSharpServiceEndpointBinderBuilderTest.Domain.cs b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/CSharp/CSharpServiceEndpointBinderBuilderTest.Domain.cs index a5d8d639..0913a6e4 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/CSharp/CSharpServiceEndpointBinderBuilderTest.Domain.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/CSharp/CSharpServiceEndpointBinderBuilderTest.Domain.cs @@ -84,7 +84,7 @@ public class WriteNewAttributeCases private static string FinishCode(string template) { return string.Format( - "new {0}.{1}{2}", + "new global::{0}.{1}{2}", typeof(CSharpServiceEndpointBinderBuilderTest).FullName, nameof(MyAttribute), template); diff --git a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.Domain.cs b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.Domain.cs index d5199369..469acd5b 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.Domain.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.Domain.cs @@ -61,7 +61,7 @@ private sealed class FullNameCases [DisplayName("int?[]")] public int?[] C6() => throw new NotImplementedException(); - [DisplayName("ServiceModel.Grpc.DesignTime.Generator.Internal.SyntaxToolsTest.I1")] + [DisplayName("global::ServiceModel.Grpc.DesignTime.Generator.Internal.SyntaxToolsTest.I1")] public I1 C7() => throw new NotImplementedException(); [DisplayName("void")] diff --git a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.cs b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.cs index 9841367f..cb0208cd 100644 --- a/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.cs +++ b/Sources/ServiceModel.Grpc.DesignTime.Test/Generator/Internal/SyntaxToolsTest.cs @@ -180,6 +180,68 @@ public void IsValueTuple(Type type, bool expected) SyntaxTools.IsValueTuple(symbol).ShouldBe(expected); } + [Test] + [TestCase(typeof(I1), false)] + [TestCase(typeof(object), false)] + [TestCase(typeof(int?), true)] + [TestCase(typeof(ValueTask?), true)] + public void IsNullable(Type type, bool expected) + { + var symbol = Compilation.GetTypeByMetadataName(type); + symbol.ShouldNotBeNull(); + + SyntaxTools.IsNullable(symbol).ShouldBe(expected); + } + + [Test] + [TestCase(typeof(ValueTuple), "string", "int")] + [TestCase(typeof(object))] + [TestCase(typeof(Task), "int")] + public void GenericTypeArguments(Type type, params string[] expected) + { + var symbol = Compilation.GetTypeByMetadataName(type); + var actual = SyntaxTools.GenericTypeArguments(symbol); + + actual.Select(SyntaxTools.GetFullName).ShouldBe(expected); + } + + [Test] + [TestCase(typeof(Task), true)] + [TestCase(typeof(Task), true)] + [TestCase(typeof(object), false)] + [TestCase(typeof(Task<(int, string)>), true)] + [TestCase(typeof(ValueTask), true)] + [TestCase(typeof(ValueTask), true)] + public void IsTask(Type type, bool expected) + { + var symbol = Compilation.GetTypeByMetadataName(type); + + SyntaxTools.IsTask(symbol).ShouldBe(expected); + } + + [Test] + [TestCase(typeof(ValueTask), true)] + [TestCase(typeof(ValueTask), true)] + [TestCase(typeof(object), false)] + [TestCase(typeof(Task), false)] + public void IsValueTask(Type type, bool expected) + { + var symbol = Compilation.GetTypeByMetadataName(type); + + SyntaxTools.IsValueTask(symbol).ShouldBe(expected); + } + + [Test] + [TestCase(typeof(IAsyncEnumerable), true)] + [TestCase(typeof(IAsyncEnumerator), false)] + [TestCase(typeof(object), false)] + public void IsAsyncEnumerable(Type type, bool expected) + { + var symbol = Compilation.GetTypeByMetadataName(type); + + SyntaxTools.IsAsyncEnumerable(symbol).ShouldBe(expected); + } + private static IEnumerable GetFullNameCases() { var type = Compilation.GetTypeByMetadataName(typeof(FullNameCases)); @@ -192,7 +254,7 @@ private static IEnumerable GetFullNameCases() expected.ShouldNotBeNull(); yield return new TestCaseData(method.ReturnType, expected) { TestName = "FullName." + method.Name }; - } + } } } } diff --git a/Sources/Versions.props b/Sources/Versions.props index afaf2e00..69515d85 100644 --- a/Sources/Versions.props +++ b/Sources/Versions.props @@ -1,6 +1,6 @@ - 1.4.5 + 1.4.6 2.45.0 2.44.0