Exemple #1
0
        internal static SafeRecursionCodeGenProps ParseFromAttribute(GeneratorExecutionContext context, MethodDeclarationSyntax methodSyntax, AttributeData attributeData)
        {
            var props = new SafeRecursionCodeGenProps();

            foreach (var(name, value) in attributeData.NamedArguments)
            {
                switch (name)
                {
                case nameof(GenerateSafeRecursionAttribute.AccessLevel):
                    props.AccessLevel = (AccessLevel)value.Value;
                    break;

                case nameof(GenerateSafeRecursionAttribute.GeneratedMethodName):
                    props.GeneratedMethodName = (string)value.Value;
                    break;

                case nameof(GenerateSafeRecursionAttribute.ExtensionMethod):
                    props.ExtensionMethod = (ExtensionMethod)value.Value;
                    break;

                case nameof(GenerateSafeRecursionAttribute.MutualRecursionId):
                    props.MutualRecursionId = (string)value.Value;
                    break;

                case nameof(GenerateSafeRecursionAttribute.ExposeAsEntryPoint):
                    props.ExposeAsEntryPoint = (bool)value.Value;
                    break;

                default:
                    context.LogInternalError(methodSyntax.GetLocation(), $"Unexpected GenerateSafeRecursionAttribute property '{name}' = '{value.Value}'");
                    break;
                }
            }

            return(props);
        }
        public void Execute(GeneratorExecutionContext context)
        {
            // retrieve the populated receiver
            if (!(context.SyntaxReceiver is RecursionSyntaxReceiver receiver))
            {
                return;
            }

            var compilation = context.Compilation;

            if ("C#" != compilation.Language)
            {
                context.ReportUnsupportedLanguageError(compilation.Language);
                return;
            }

            var goodCandidates = receiver.CandidateMethods.Select(methodSyntax =>
            {
                var methodModel  = compilation.GetSemanticModel(methodSyntax.SyntaxTree);
                var methodSymbol = methodModel.GetDeclaredSymbol(methodSyntax);
                var attr         = methodSymbol.GetAttributes().SingleOrDefault(a => a.AttributeClass.MatchesRuntimeType(TargetAttribute));
                var codeGenProps = (attr != null) ? SafeRecursionCodeGenProps.ParseFromAttribute(context, methodSyntax, attr) : null;
                return(new RecursiveMethodInfo(methodSyntax, methodSymbol, methodModel, codeGenProps));
            })
                                 .Where(t => t.CodeGenProps != null)
                                 .GroupBy(t => t.CodeGenProps.MutualRecursionId ?? "") //convert null to empty string for GroupBy
                                 .ToDictionary(g => g.Key, g => g.ToList());

            if (!goodCandidates.TryGetValue("", out var simpleCandidates))
            {
                simpleCandidates = new List <RecursiveMethodInfo>();
            }

            context.Log($"Found {simpleCandidates.Count} method(s) for simple recursion and {goodCandidates.Count - 1} method group(s) for mutual recursion generation.");

            foreach (var methodInfo in simpleCandidates)
            {
                try
                {
                    var generatedCode = CalculatorClassGenerator.GenerateSimpleSafeRec(context, methodInfo);
                    if (generatedCode != null)
                    {
                        context.AddSource($"{methodInfo.MethodSymbol.ContainingType.Name}_{methodInfo.MethodSymbol.Name}_SafeRecursion.cs",
                                          SourceText.From(generatedCode, Encoding.UTF8));
                    }
                }
                catch (Exception e)
                {
                    context.LogInternalError(methodInfo.MethodSyntax.GetLocation(),
                                             $"Processing '{methodInfo.MethodSymbol.ContainingType.Name}' resulted in an internal error '{e}'");
                }
            }

            foreach (var(recursionId, methodInfos) in goodCandidates)
            {
                // skip the simple candidates
                if (string.IsNullOrEmpty(recursionId))
                {
                    continue;
                }
                var someMethodInfo = methodInfos.First();
                try
                {
                    var generatedCode = CalculatorClassGenerator.GenerateMutualSafeRec(context, methodInfos);
                    if (generatedCode != null)
                    {
                        context.AddSource($"{someMethodInfo.MethodSymbol.ContainingType.Name}_{recursionId}_SafeRecursion.cs",
                                          SourceText.From(generatedCode, Encoding.UTF8));
                    }
                }
                catch (Exception e)
                {
                    context.LogInternalError(someMethodInfo.MethodSyntax.Parent.GetLocation(),
                                             $"Processing '{recursionId}' mutual recursion resulted in an internal error '{e}'");
                }
            }
        }