public override IEnumerable <IDiagnostic> AnalyzeInternal(SemanticModel model) { Lazy <ImmutableDictionary <DeclaredSymbol, ImmutableHashSet <ResourceDependency> > > inferredDependenciesMap = new Lazy <ImmutableDictionary <DeclaredSymbol, ImmutableHashSet <ResourceDependency> > >( () => ResourceDependencyVisitor.GetResourceDependencies( model, new ResourceDependencyVisitor.Options { IgnoreExplicitDependsOn = true })); var visitor = new ResourceVisitor(this, inferredDependenciesMap, model); visitor.Visit(model.SourceFile.ProgramSyntax); return(visitor.diagnostics); }
public static BicepDeploymentGraph CreateDeploymentGraph(CompilationContext context, string entryFilePath) { var nodes = new List <BicepDeploymentGraphNode>(); var edges = new List <BicepDeploymentGraphEdge>(); var queue = new Queue <(SemanticModel, string, string?)>(); var entrySemanticModel = context.Compilation.GetEntrypointSemanticModel(); queue.Enqueue((entrySemanticModel, entryFilePath, null)); while (queue.Count > 0) { var(semanticModel, filePath, parentId) = queue.Dequeue(); var nodesBySymbol = new Dictionary <DeclaredSymbol, BicepDeploymentGraphNode>(); var dependenciesBySymbol = ResourceDependencyVisitor.GetResourceDependencies(semanticModel) .Where(x => x.Key.Name != LanguageConstants.MissingName && x.Key.Name != LanguageConstants.ErrorName) .ToImmutableDictionary(x => x.Key, x => x.Value); var errors = semanticModel.GetAllDiagnostics().Where(x => x.Level == DiagnosticLevel.Error).ToList(); // Create nodes. foreach (var symbol in dependenciesBySymbol.Keys) { var id = parentId is null ? symbol.Name : $"{parentId}::{symbol.Name}"; if (symbol is ResourceSymbol resourceSymbol) { var resourceType = resourceSymbol.TryGetResourceTypeReference()?.FullyQualifiedType ?? "<unknown>"; var isCollection = resourceSymbol.IsCollection; var resourceSpan = resourceSymbol.DeclaringResource.Span; var range = resourceSpan.ToRange(context.LineStarts); var resourceHasError = errors.Any(error => TextSpan.AreOverlapping(resourceSpan, error.Span)); nodesBySymbol[symbol] = new BicepDeploymentGraphNode(id, resourceType, isCollection, range, false, resourceHasError, filePath); } if (symbol is ModuleSymbol moduleSymbol) { var directory = Path.GetDirectoryName(filePath); var moduleRelativePath = moduleSymbol.DeclaringModule.TryGetPath()?.TryGetLiteralValue(); var moduleFilePath = directory is not null && moduleRelativePath is not null ? Path.GetFullPath(Path.Combine(directory, moduleRelativePath)) : null; var isCollection = moduleSymbol.IsCollection; var moduleSpan = moduleSymbol.DeclaringModule.Span; var range = moduleSpan.ToRange(context.LineStarts); var moduleHasError = errors.Any(error => TextSpan.AreOverlapping(moduleSpan, error.Span)); var hasChildren = false; if (moduleFilePath is not null && moduleSymbol.TryGetSemanticModel(out var moduleSemanticModel, out var _) && moduleSemanticModel is SemanticModel bicepModel && (bicepModel.Root.ResourceDeclarations.Any() || bicepModel.Root.ModuleDeclarations.Any())) { hasChildren = true; queue.Enqueue((bicepModel, moduleFilePath, id)); } nodesBySymbol[symbol] = new BicepDeploymentGraphNode(id, "<module>", isCollection, range, hasChildren, moduleHasError, moduleFilePath); } } nodes.AddRange(nodesBySymbol.Values); // Create edges. foreach (var(symbol, dependencies) in dependenciesBySymbol) { if (!nodesBySymbol.TryGetValue(symbol, out var source)) { continue; } foreach (var dependency in dependencies) { if (!nodesBySymbol.TryGetValue(dependency.Resource, out var target)) { continue; } edges.Add(new BicepDeploymentGraphEdge(source.Id, target.Id)); } } } return(new BicepDeploymentGraph( nodes.OrderBy(node => node.Id), edges.OrderBy(edge => $"{edge.SourceId}>{edge.TargetId}"), entrySemanticModel.GetAllDiagnostics().Count(x => x.Level == DiagnosticLevel.Error))); }