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); }
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)); } } }); }); }