namespace Spectre.Console.Analyzer; /// /// Analyzer to detect calls to live renderables within a live renderable context. /// [DiagnosticAnalyzer(LanguageNames.CSharp)] [Shared] public class NoConcurrentLiveRenderablesAnalyzer : SpectreAnalyzer { private static readonly DiagnosticDescriptor _diagnosticDescriptor = Descriptors.S1020_AvoidConcurrentCallsToMultipleLiveRenderables; /// public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_diagnosticDescriptor); /// protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext) { compilationStartContext.RegisterOperationAction( context => { var invocationOperation = (IInvocationOperation)context.Operation; var methodSymbol = invocationOperation.TargetMethod; const string StartMethod = "Start"; if (methodSymbol.Name != StartMethod) { return; } var liveTypes = Constants.LiveRenderables .Select(i => context.Compilation.GetTypeByMetadataName(i)) .ToImmutableArray(); if (liveTypes.All(i => !SymbolEqualityComparer.Default.Equals(i, methodSymbol.ContainingType))) { return; } #pragma warning disable RS1030 // Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer var model = context.Compilation.GetSemanticModel(context.Operation.Syntax.SyntaxTree); #pragma warning restore RS1030 // Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer var parentInvocations = invocationOperation .Syntax.Ancestors() .OfType() .Select(i => model.GetOperation(i)) .OfType() .ToList(); if (parentInvocations.All(parent => parent.TargetMethod.Name != StartMethod || !liveTypes.Contains(parent.TargetMethod.ContainingType))) { return; } var displayString = SymbolDisplay.ToDisplayString( methodSymbol, SymbolDisplayFormat.CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None)); context.ReportDiagnostic( Diagnostic.Create( _diagnosticDescriptor, invocationOperation.Syntax.GetLocation(), displayString)); }, OperationKind.Invocation); } }