private static void AddTaskUtilities( RecordBuilder recordBuilder, TypeSyntax tcsType) { var tcs = GeneratorHelper.GenericName( "TaskCompletionSource", tcsType); var initializer = ExpressionGenerationHelper.CreateObject(tcs); recordBuilder.AddField(tcs, "result", initializer); var resolveMethod = new MethodBuilder(GH.IdentifierToken("Resolve")) .Modifiers(Modifiers.Public) .AddParameter(tcsType, GH.IdentifierToken("value")) .Body(new BodyBuilder() .AddVoidMemberInvocation( GH.Identifier("result"), GH.Identifier("TrySetResult"), SF.Argument(GH.Identifier("value"))) .Build()) .Build(); var cancelMethod = new MethodBuilder(GH.IdentifierToken("Cancel")) .Modifiers(Modifiers.Public) .Body(new BodyBuilder() .AddVoidMemberInvocation( GH.Identifier("result"), GH.Identifier("TrySetCanceled")) .Build()) .Build(); var rejectMethod = new MethodBuilder(GH.IdentifierToken("Reject")) .Modifiers(Modifiers.Public) .AddParameter(GH.Identifier("Exception"), GH.IdentifierToken("exc")) .Body(new BodyBuilder() .AddVoidMemberInvocation( GH.Identifier("result"), GH.Identifier("TrySetException"), SF.Argument(GH.Identifier("exc"))) .Build()) .Build(); recordBuilder.AddMethod(resolveMethod); recordBuilder.AddMethod(cancelMethod); recordBuilder.AddMethod(rejectMethod); }
private static async Task <Document> GenerateCreateMethod( Document document, ConstructorDeclarationSyntax constructor, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var classDeclaration = constructor .Parent.FirstAncestorOrSelf <ClassDeclarationSyntax>(); if (classDeclaration == null) { return(document); } var classType = SyntaxFactory.IdentifierName( classDeclaration.Identifier.WithoutTrivia()); var createObjectExpression = ExpressionGenerationHelper.CreateObject( classType, constructor.ParameterList.ToArgumentArray()); var createMethodExpression = MethodGenerationHelper.Builder(CreateMethodName) .Modifiers(Modifiers.Public, Modifiers.Static) .Parameters(constructor.ParameterList.Parameters.ToArray()) .ReturnType(SyntaxFactory.IdentifierName(classDeclaration.Identifier.WithoutTrivia())) .ArrowBody(ExpressionGenerationHelper.Arrow(createObjectExpression)) .Build(); // TODO: validate argument types as well ? var previousCreate = ClassDeclarationSyntaxAnalysis.GetMembers <MethodDeclarationSyntax>(classDeclaration) .FirstOrDefault(m => m.Identifier.ValueText.Equals(CreateMethodName) && m.ParameterList.Parameters.Count == createMethodExpression.ParameterList.Parameters.Count); // TODO: align with "with" method generation var newClassDeclaration = classDeclaration; if (previousCreate == null) { createMethodExpression = createMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithLeadingTrivia( Settings.EndOfLine, SyntaxFactory.ElasticSpace) .WithTrailingTrivia(Settings.EndOfLine); newClassDeclaration = newClassDeclaration.InsertNodesAfter(constructor, new[] { createMethodExpression }); } else { createMethodExpression = createMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithTriviaFrom(previousCreate); newClassDeclaration = newClassDeclaration.ReplaceNode(previousCreate, createMethodExpression); } var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(classDeclaration, newClassDeclaration); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument); }
private static async Task <Document> GenerateWithMethods( Document document, ClassDeclarationSyntax classDeclarationSyntax, IList <PropertyDeclarationSyntax> properties, int[] propertyToParameterIdx, CancellationToken cancellationToken) { int MappedIdx(int i) { int mappedIdx = propertyToParameterIdx[i]; if (mappedIdx == -1) { throw new ArgumentException($"{nameof(propertyToParameterIdx)} contains invalid mapping"); } return(mappedIdx); } ArgumentSyntax[] ReorderArgumentsToMapping(IReadOnlyList <ArgumentSyntax> arguments) { var result = new ArgumentSyntax[arguments.Count]; for (int i = 0; i < arguments.Count; ++i) { result[MappedIdx(i)] = arguments[i]; } return(result); } T ExecuteWithTempArg <T>(ArgumentSyntax[] args, int argIdx, ArgumentSyntax tempArg, Func <ArgumentSyntax[], T> operation) { var oldArg = args[argIdx]; args[argIdx] = tempArg; var result = operation(args); args[argIdx] = oldArg; return(result); } if (properties.Count != propertyToParameterIdx.Length) { throw new ArgumentException("properties.Count != propertyToParameterIdx.Length"); } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (properties.Count == 0) { return(document); } var classType = SyntaxFactory.IdentifierName(classDeclarationSyntax.Identifier.WithoutTrivia()); var newClassDeclaration = classDeclarationSyntax; var argumentsArray = properties.Select(p => p.ToArgument()).ToArray(); argumentsArray = ReorderArgumentsToMapping(argumentsArray); for (int i = 0; i < properties.Count; ++i) { var p = properties[i]; var id = p.Identifier.WithoutTrivia(); var lcId = SyntaxHelpers.LowercaseIdentifierFirstLetter(id); var methodName = WithRefactoringUtils.MethodName(p.Identifier); var arg = SyntaxHelpers.ArgumentFromIdentifier(lcId); var objectCreation = ExecuteWithTempArg(argumentsArray, MappedIdx(i), arg, args => ExpressionGenerationHelper.CreateObject(classType, args)); var withMethodExpression = MethodGenerationHelper.Builder(methodName) .Modifiers(Modifiers.Public) .Parameters(SyntaxHelpers.Parameter(p.Type, lcId)) .ReturnType(SyntaxFactory.IdentifierName(classDeclarationSyntax.Identifier.WithoutTrivia())) .ArrowBody(ExpressionGenerationHelper.Arrow(objectCreation)) .Build(); var previousWith = ClassDeclarationSyntaxAnalysis.GetMembers <MethodDeclarationSyntax>( newClassDeclaration) .FirstOrDefault(m => m.Identifier.ValueText.Equals(methodName)); if (previousWith == null) { // TODO: Group with members next to each other rather than adding at the end withMethodExpression = withMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithLeadingTrivia( Settings.EndOfLine, SyntaxFactory.ElasticSpace) .WithTrailingTrivia(Settings.EndOfLine); newClassDeclaration = newClassDeclaration.AddMembers(withMethodExpression); } else { withMethodExpression = withMethodExpression .NormalizeWhitespace(elasticTrivia: false) .WithTriviaFrom(previousWith); newClassDeclaration = newClassDeclaration.ReplaceNode(previousWith, withMethodExpression); } } var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(classDeclarationSyntax, newClassDeclaration); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument); }
public async Task Test2() { // Arrange var text = @" using System; using System.Threading.Tasks; namespace RefactorClasses.Analysis.Test { [StateMachine(ContextType = typeof(ContextBase), StateType = typeof(StateBase), TriggerType = (typeof(TriggerBase)))] public class StateMachineImpl { public void DoSomething( int a, Test1 testClass, string fdeee) { } public async Task<int> TaskMethodReturningSomething(int a, float b) { return 10; } public System.Threading.Tasks.Task AsyncOperationsSupport(int a, float b) { return Task.CompletedTask; } public async Task TaskMethod(int a, float b) { return; } public async Task TaskMethodWithArrays(int[] a, float[] b) { return; } public async Task TaskMethodWithTuples((int, float) a, float[] b) { return; } private void PrintSomething() {} } public class TriggerBase { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public sealed class StateMachineAttribute : Attribute { public Type StateType { get; set; } public Type TriggerType { get; set; } public Type ContextType { get; set; } } }"; var tree = CSharpSyntaxTree.ParseText(text); var compilation = TestHelpers.CreateCompilation(tree); var semanticModel = compilation.GetSemanticModel(tree); var classDeclaration = TestHelpers.FindFirstClassDeclaration(await tree.GetRootAsync()); var classInspector = new ClassInspector(classDeclaration); var semanticInspector = classInspector.CreateSemanticQuery(semanticModel); bool foundAttribute = semanticInspector.TryFindFirstAttributeMatching( "StateMachineAttribute", out var atData); var triggerType = atData ?.NamedArguments .FirstOrDefault(kvp => kvp.Key.Equals("TriggerType")); if (triggerType == null) { return; } var methods = classInspector.FindMatchingMethods( mi => mi.Check(m => m.IsPublic() && !m.IsStatic()).Passed); foreach (var method in methods) { var msq = method.CreateSemanticQuery(semanticModel); var returnType = msq.GetReturnType(); var isTaskReturn = IsTask(returnType.Symbol); var parameters = method.Parameters.Select(par => par.Type).ToList(); // TODO: will throw if array var triggerTypeName = triggerType.Value.Value.Value as INamedTypeSymbol; if (triggerTypeName == null) { return; } var recordBuilder = new RecordBuilder(method.Name) .AddModifiers(Modifiers.Public) .AddBaseTypes(GeneratorHelper.Identifier(triggerTypeName.Name)) .AddProperties( method.Parameters .Select(p => (p.Type, p.Name)).ToArray()); if (isTaskReturn.Value.IsTask()) { var boolTcs = GeneratorHelper.GenericName( "TaskCompletionSource", Types.Bool); var initializer = ExpressionGenerationHelper.CreateObject(boolTcs); recordBuilder.AddField(boolTcs, "result", initializer); var resolveMethod = new MethodBuilder(GH.IdentifierToken("Resolve")) .Body(new BodyBuilder() .AddVoidMemberInvocation( GH.Identifier("result"), GH.Identifier("TrySetResult"), SF.Argument(GH.Identifier("true"))) .Build()) .Build(); var cancelMethod = new MethodBuilder(GH.IdentifierToken("Cancel")) .Body(new BodyBuilder() .AddVoidMemberInvocation( GH.Identifier("result"), GH.Identifier("TrySetCanceled")) .Build()) .Build(); var rejectMethod = new MethodBuilder(GH.IdentifierToken("Cancel")) .AddParameter(GH.Identifier("Exception"), GH.IdentifierToken("exc")) .Body(new BodyBuilder() .AddVoidMemberInvocation( GH.Identifier("result"), GH.Identifier("TrySetException"), SF.Argument(GH.Identifier("exc"))) .Build()) .Build(); recordBuilder.AddMethod(resolveMethod); recordBuilder.AddMethod(cancelMethod); recordBuilder.AddMethod(rejectMethod); int ddddd = 0; } else if (isTaskReturn.Value.IsTypedTask(out var taskType)) { var typedTcs = GeneratorHelper.GenericName( "TaskCompletionSource", GeneratorHelper.Identifier(taskType.Name)); var initializer = ExpressionGenerationHelper.CreateObject(typedTcs); recordBuilder.AddField(typedTcs, "result", initializer); } var record = recordBuilder.Build(); // TODO: if task is returned -> generate TaskCompletionSource // and matching methods var rs = record.ToString(); int a = 10; } // Act // Assert IsTaskResult?IsTask(ISymbol symbol) { var namedSymbol = symbol as INamedTypeSymbol; if (namedSymbol == null) { return(null); } if (namedSymbol.Name == "Task" && namedSymbol?.ContainingNamespace?.ToString() == "System.Threading.Tasks") { var firstTypeArg = namedSymbol.TypeArguments.FirstOrDefault(); if (firstTypeArg != null) { return(IsTaskResult.TypedTask(firstTypeArg)); } else { return(IsTaskResult.Task()); } } return(IsTaskResult.NotATask()); } //var tcs = new TaskCompletionSource<int>(); //tcs.TrySetException() }