public Task <SyntaxList <MemberDeclarationSyntax> > GenerateAsync( TransformationContext context, IProgress <Diagnostic> progress, CancellationToken cancellationToken) { if (context.ProcessingNode is TypeDeclarationSyntax applyTo && (applyTo is ClassDeclarationSyntax || (applyTo is StructDeclarationSyntax))) { var record = CodeGenUtil.MakeCaseType( context, applyTo.Identifier, applyTo.Members, applyTo.TypeParameterList, applyTo.Modifiers, applyTo.ConstraintClauses, applyTo.Identifier, null, CodeGenUtil.GetState(context, progress, AllowedType.ClassOrStruct, "Record code-gen").Fields, BaseSpec.None, caseIsClass: applyTo is ClassDeclarationSyntax, caseIsPartial: true, -1); return(Task.FromResult(List <MemberDeclarationSyntax>().Add(record))); }
public Task <SyntaxList <MemberDeclarationSyntax> > GenerateAsync( TransformationContext context, IProgress <Diagnostic> progress, CancellationToken cancellationToken) { try { if (context.ProcessingNode is InterfaceDeclarationSyntax applyTo) { if (applyTo.TypeParameterList.Parameters.Count > 1) { CodeGenUtil.ReportError($"Free monads must have only one generic argument", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } if (applyTo.TypeParameterList.Parameters.Count == 0) { CodeGenUtil.ReportError($"Free monads must a generic argument", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } var genA = applyTo.TypeParameterList.Parameters.First().ToString(); var genB = CodeGenUtil.NextGenName(genA); var genC = CodeGenUtil.NextGenName(genB); var thisType = ParseTypeName($"{applyTo.Identifier.Text}{applyTo.TypeParameterList}"); var typeA = MakeTypeName(applyTo.Identifier.Text, genA); var typeB = MakeTypeName(applyTo.Identifier.Text, genB); var mapFunc = ParseTypeName($"System.Func<{genA}, {genB}>"); var mapIsStatic = applyTo.Members .OfType <MethodDeclarationSyntax>() .Where(m => m.Identifier.Text == "Map") .Where(m => m.ParameterList.Parameters.Count == 2) .Where(m => m.Modifiers.Any(mo => mo.IsKind(SyntaxKind.StaticKeyword))) .Where(m => m.Modifiers.Any(mo => mo.IsKind(SyntaxKind.PublicKeyword))) // .Where(m => m.ReturnType.IsEquivalentTo(typeB)) // .Where(m => m.ParameterList.Parameters[0].Type.IsEquivalentTo(typeA)) // .Where(m => m.ParameterList.Parameters[1].Type.IsEquivalentTo(mapFunc)) .Any(); var caseMethods = applyTo.Members .OfType <MethodDeclarationSyntax>() .Where(m => !m.Modifiers.Any(mo => mo.IsKind(SyntaxKind.StaticKeyword))) .Select(m => MakeFree(m, thisType)) .ToArray(); var firstPure = caseMethods.Where(HasPureAttr) .Where(m => m.ParameterList.Parameters.Count == 1) .Where(m => m.ReturnType.IsEquivalentTo(typeA)) .Where(m => m.ParameterList.Parameters.First().Type.ToString() == genA) .FirstOrDefault(); if (firstPure == null) { CodeGenUtil.ReportError($"Type can't be made into a free monad because no method in the interface has [Pure] attribute that takes a single argument of type '{genA}' and returns a '{genA}'", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } var caseRes = caseMethods .Zip(Enumerable.Range(1, int.MaxValue), (m, i) => (m, i)) .Select(m => CodeGenUtil.MakeCaseType( context, progress, applyTo.Identifier, applyTo.Members, applyTo.TypeParameterList, applyTo.Modifiers, applyTo.ConstraintClauses, m.m.Identifier, m.m.TypeParameterList, m.m.ParameterList .Parameters .Select(p => (p.Identifier, p.Type, p.Modifiers, p.AttributeLists)) .ToList(), BaseSpec.Interface, caseIsClass: true, caseIsPartial: false, includeWithAndLenses: false, m.i)) .ToList(); var ok = caseRes.All(x => x.Success); var cases = caseRes.Select(c => c.Type); var staticCtorClass = MakeStaticClass( applyTo.Identifier, caseMethods, applyTo.TypeParameterList, applyTo.ConstraintClauses, firstPure, mapIsStatic); if (ok) { return(Task.FromResult(List <MemberDeclarationSyntax>().AddRange(cases).Add(staticCtorClass))); } else { return(Task.FromResult(List <MemberDeclarationSyntax>())); } } else { CodeGenUtil.ReportError($"Type can't be made into a free monad. It must be an interface", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } } catch (Exception e) { CodeGenUtil.ReportError(e.Message, "Free monad Code-Gen", context.ProcessingNode, progress); foreach (var line in e.StackTrace.Split('\n')) { CodeGenUtil.ReportError(line, "Free monad Code-Gen", context.ProcessingNode, progress); } return(Task.FromResult(List <MemberDeclarationSyntax>())); } }