public static bool TryGetAnalysis(ReflectionTypeSymbolLocator locator, CancellationToken ct, [NotNullWhen(true)] out DiagnosticGroupTypeAnalysis?analysis)
 {
     if (!locator.EnsureCompilationHasRequiredTypes())
     {
         analysis = null;
         return(false);
     }
     analysis = new DiagnosticGroupTypeAnalysis(locator, ct);
     return(true);
 }
Esempio n. 2
0
        public void Execute(GeneratorExecutionContext context)
        {
            try {
                if (context.SyntaxReceiver is not SyntaxCapture capture)
                {
                    return;
                }
                var locator = new ReflectionTypeSymbolLocator(context.Compilation);
                if (!DiagnosticGroupTypeAnalysis.TryGetAnalysis(locator, context.CancellationToken, out var typeAnalysis))
                {
                    return;
                }
                if (!DiagnosticMemberTypeAnalysis.TryGetAnalysis(locator, context.CancellationToken, out var memberAnalysis))
                {
                    return;
                }
                var ddList = new List <DiagnosticTypeInfo>();
                foreach (var(type, typeAttr) in typeAnalysis.GatherValidTypes(capture.CapturedSyntaxes, context.ReportDiagnostic))
                {
                    var members = type.GetMembers()
                                  .Select(member => (member, Attribute: memberAnalysis.GetMemberSymbolAttribute(member, context.ReportDiagnostic)))
                                  .Where(member => member.Attribute is { })
                                  .ToDictionary(x => x.member, x => x.Attribute);
                    if (members.Count == 0)
                    {
                        // DIAG : Warn no member, generation will not procede
                        context.ReportDiagnostic(DiagnosticSource.TypeHasNoMember(type));
                        continue;
                    }
                    var info = new DiagnosticTypeInfo {
                        ContainingSymbol  = type,
                        GroupAttribute    = typeAttr,
                        DescriptorSymbols = members !
                    };
                    ddList.Add(info);
                    //var code = DiagnosticGroupCodeBuilder.Generate(info);
                    var code       = new DiagnosticGroupCodeBuilder(locator, info).ToString();
                    var sourceText = SourceText.From(code, Encoding.UTF8);
#if DEBUG
                    Directory.CreateDirectory("./.generated");
                    File.WriteAllText($"./.generated/{type}.cs", code);
                    File.WriteAllText($"./.generated/debug.{type}.cs", $"{locator.GetAssemblySymbol(typeof(List<>).Assembly)} {locator._compilingCoreLib} e {locator._executionCoreLib}");
#endif
                    context.AddSource($"{type}.diagnostics.generated", sourceText);
                }
                ReportDuplicate(context, ddList);
            }
        public override void Initialize(AnalysisContext context)
        {
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.EnableConcurrentExecution();
            context.RegisterCompilationStartAction(startCompilation => {
                var locator = new ReflectionTypeSymbolLocator(startCompilation.Compilation);
                var attr    = new ConcurrentBag <(DiagnosticDescriptor Descriptor, ISymbol Member)>();

                if (!DiagnosticGroupTypeAnalysis.TryGetAnalysis(locator, startCompilation.CancellationToken, out var typeAnalysis))
                {
                    return;
                }
                if (!DiagnosticMemberTypeAnalysis.TryGetAnalysis(locator, startCompilation.CancellationToken, out var memberAnalysis))
                {
                    return;
                }

                startCompilation.RegisterSymbolAction(context => {
                    if (!(context.Symbol is INamedTypeSymbol namedTypeSymbol))
                    {
                        return;
                    }
                    if (!context.Symbol.GetAttributes().Any(x => x.AttributeClass.Equals <DiagnosticGroupAttribute>(locator)))
                    {
                        return;
                    }
                    var group = typeAnalysis.VerifyType(namedTypeSymbol, context.ReportDiagnostic);
                    foreach (var member in namedTypeSymbol.GetMembers().Where(x => x is IPropertySymbol || x is IFieldSymbol))
                    {
                        var descriptionAttribute = memberAnalysis.GetMemberSymbolAttribute(member, context.ReportDiagnostic);
                        if (descriptionAttribute is null || group is null)
                        {
                            continue;
                        }
                        attr.Add((descriptionAttribute.GetDescriptor(group), member));
                    }
                }, SymbolKind.NamedType);

                startCompilation.RegisterSymbolAction(context => {
                    if (!(context.Symbol is IPropertySymbol) && !(context.Symbol is IFieldSymbol))
                    {
                        return;
                    }
                    // Inspect if member has DiagnosticDescriptionAttribute
                    var memberAttr = context.Symbol.GetAttributes().FirstOrDefault(x => x.AttributeClass.Equals <DiagnosticDescriptionAttribute>(locator));
                    if (memberAttr is null)
                    {
                        return;
                    }
                    if (!(context.Symbol.ContainingType is INamedTypeSymbol hostingType))
                    {
                        return;
                    }
                    if (hostingType.GetAttributes().Any(x => x.AttributeClass.Equals <DiagnosticGroupAttribute>(locator)))
                    {
                        return;
                    }
                    // Report diagnostic on how the diagnostic group is missing on the tpye
                    var memberAttrLocation = memberAttr.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken).GetLocation();
                    context.ReportDiagnostic(DiagnosticSource.MemberShouldBeInGroup(context.Symbol, memberAttrLocation));
                }, SymbolKind.Property, SymbolKind.Field);

                startCompilation.RegisterCompilationEndAction(context => {
                    foreach (var group in attr.GroupBy(x => x.Descriptor.Id))
                    {
                        if (group.Count() <= 1)
                        {
                            continue;
                        }
                        foreach (var(descriptor, member) in group.ToList())
                        {
                            context.ReportDiagnostic(DiagnosticSource.DuplicateDiagnosticDescriptor(member, group.Select(x => x.Member), descriptor.Id));
                        }
                    }
                });
            });
        }