// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; namespace Roslyn.Utilities; /// <summary> /// An AnnotationTable helps you attach your own annotation types/instances to syntax. /// /// It maintains a map between your instances and actual SyntaxAnnotation's used to annotate the nodes /// and offers an API that matches the true annotation API on SyntaxNode. /// /// The table controls the lifetime of when you can find and retrieve your annotations. You won't be able to /// find your annotations via HasAnnotations/GetAnnotations unless you use the same annotation table for these operations /// that you used for the WithAdditionalAnnotations operation. /// /// Your custom annotations are not serialized with the syntax tree, so they won't move across boundaries unless the /// same AnnotationTable is available on both ends. /// /// also, note that this table is not thread safe. /// </summary> internal sealed class AnnotationTable<TAnnotation>(string annotationKind) where TAnnotation : class { private int _globalId; private readonly Dictionary<TAnnotation, SyntaxAnnotation> _realAnnotationMap = []; private readonly Dictionary<string, TAnnotation> _annotationMap = []; private IEnumerable<SyntaxAnnotation> GetOrCreateRealAnnotations(TAnnotation[] annotations) { foreach (var annotation in annotations) { yield return this.GetOrCreateRealAnnotation(annotation); } } private SyntaxAnnotation GetOrCreateRealAnnotation(TAnnotation annotation) { if (!_realAnnotationMap.TryGetValue(annotation, out var realAnnotation)) { var id = Interlocked.Increment(ref _globalId); var idString = id.ToString(); realAnnotation = new SyntaxAnnotation(annotationKind, idString); _annotationMap.Add(idString, annotation); _realAnnotationMap.Add(annotation, realAnnotation); } return realAnnotation; } private IEnumerable<SyntaxAnnotation> GetRealAnnotations(TAnnotation[] annotations) { foreach (var annotation in annotations) { var realAnnotation = this.GetRealAnnotation(annotation); if (realAnnotation != null) { yield return realAnnotation; } } } private SyntaxAnnotation? GetRealAnnotation(TAnnotation annotation) { _realAnnotationMap.TryGetValue(annotation, out var realAnnotation); return realAnnotation; } public TSyntaxNode WithAdditionalAnnotations<TSyntaxNode>(TSyntaxNode node, params TAnnotation[] annotations) where TSyntaxNode : SyntaxNode => node.WithAdditionalAnnotations([.. this.GetOrCreateRealAnnotations(annotations)]); public SyntaxToken WithAdditionalAnnotations(SyntaxToken token, params TAnnotation[] annotations) => token.WithAdditionalAnnotations([.. this.GetOrCreateRealAnnotations(annotations)]); public SyntaxTrivia WithAdditionalAnnotations(SyntaxTrivia trivia, params TAnnotation[] annotations) => trivia.WithAdditionalAnnotations([.. this.GetOrCreateRealAnnotations(annotations)]); public SyntaxNodeOrToken WithAdditionalAnnotations(SyntaxNodeOrToken nodeOrToken, params TAnnotation[] annotations) => nodeOrToken.WithAdditionalAnnotations([.. this.GetOrCreateRealAnnotations(annotations)]); public TSyntaxNode WithoutAnnotations<TSyntaxNode>(TSyntaxNode node, params TAnnotation[] annotations) where TSyntaxNode : SyntaxNode => node.WithoutAnnotations([.. GetRealAnnotations(annotations)]); public SyntaxToken WithoutAnnotations(SyntaxToken token, params TAnnotation[] annotations) => token.WithoutAnnotations([.. GetRealAnnotations(annotations)]); public SyntaxTrivia WithoutAnnotations(SyntaxTrivia trivia, params TAnnotation[] annotations) => trivia.WithoutAnnotations([.. GetRealAnnotations(annotations)]); public SyntaxNodeOrToken WithoutAnnotations(SyntaxNodeOrToken nodeOrToken, params TAnnotation[] annotations) => nodeOrToken.WithoutAnnotations([.. GetRealAnnotations(annotations)]); private IEnumerable<TAnnotation> GetAnnotations(IEnumerable<SyntaxAnnotation> realAnnotations) { foreach (var ra in realAnnotations) { Contract.ThrowIfNull(ra.Data); if (_annotationMap.TryGetValue(ra.Data, out var annotation)) { yield return annotation; } } } public IEnumerable<TAnnotation> GetAnnotations(SyntaxNode node) => GetAnnotations(node.GetAnnotations(annotationKind)); public IEnumerable<TAnnotation> GetAnnotations(SyntaxToken token) => GetAnnotations(token.GetAnnotations(annotationKind)); public IEnumerable<TAnnotation> GetAnnotations(SyntaxTrivia trivia) => GetAnnotations(trivia.GetAnnotations(annotationKind)); public IEnumerable<TAnnotation> GetAnnotations(SyntaxNodeOrToken nodeOrToken) => GetAnnotations(nodeOrToken.GetAnnotations(annotationKind)); public IEnumerable<TSpecificAnnotation> GetAnnotations<TSpecificAnnotation>(SyntaxNode node) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(node).OfType<TSpecificAnnotation>(); public IEnumerable<TSpecificAnnotation> GetAnnotations<TSpecificAnnotation>(SyntaxToken token) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(token).OfType<TSpecificAnnotation>(); public IEnumerable<TSpecificAnnotation> GetAnnotations<TSpecificAnnotation>(SyntaxTrivia trivia) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(trivia).OfType<TSpecificAnnotation>(); public IEnumerable<TSpecificAnnotation> GetAnnotations<TSpecificAnnotation>(SyntaxNodeOrToken nodeOrToken) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(nodeOrToken).OfType<TSpecificAnnotation>(); public bool HasAnnotations(SyntaxNode node) => node.HasAnnotations(annotationKind); public bool HasAnnotations(SyntaxToken token) => token.HasAnnotations(annotationKind); public bool HasAnnotations(SyntaxTrivia trivia) => trivia.HasAnnotations(annotationKind); public bool HasAnnotations(SyntaxNodeOrToken nodeOrToken) => nodeOrToken.HasAnnotations(annotationKind); public bool HasAnnotations<TSpecificAnnotation>(SyntaxNode node) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(node).OfType<TSpecificAnnotation>().Any(); public bool HasAnnotations<TSpecificAnnotation>(SyntaxToken token) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(token).OfType<TSpecificAnnotation>().Any(); public bool HasAnnotations<TSpecificAnnotation>(SyntaxTrivia trivia) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(trivia).OfType<TSpecificAnnotation>().Any(); public bool HasAnnotations<TSpecificAnnotation>(SyntaxNodeOrToken nodeOrToken) where TSpecificAnnotation : TAnnotation => this.GetAnnotations(nodeOrToken).OfType<TSpecificAnnotation>().Any(); public bool HasAnnotation(SyntaxNode node, TAnnotation annotation) => node.HasAnnotation(this.GetRealAnnotation(annotation)); public bool HasAnnotation(SyntaxToken token, TAnnotation annotation) => token.HasAnnotation(this.GetRealAnnotation(annotation)); public bool HasAnnotation(SyntaxTrivia trivia, TAnnotation annotation) => trivia.HasAnnotation(this.GetRealAnnotation(annotation)); public bool HasAnnotation(SyntaxNodeOrToken nodeOrToken, TAnnotation annotation) => nodeOrToken.HasAnnotation(this.GetRealAnnotation(annotation)); public IEnumerable<SyntaxNodeOrToken> GetAnnotatedNodesAndTokens(SyntaxNode node) => node.GetAnnotatedNodesAndTokens(annotationKind); public IEnumerable<SyntaxNode> GetAnnotatedNodes(SyntaxNode node) => node.GetAnnotatedNodesAndTokens(annotationKind).Where(nt => nt.IsNode).Select(nt => nt.AsNode()!); public IEnumerable<SyntaxToken> GetAnnotatedTokens(SyntaxNode node) => node.GetAnnotatedNodesAndTokens(annotationKind).Where(nt => nt.IsToken).Select(nt => nt.AsToken()); public IEnumerable<SyntaxTrivia> GetAnnotatedTrivia(SyntaxNode node) => node.GetAnnotatedTrivia(annotationKind); public IEnumerable<SyntaxNodeOrToken> GetAnnotatedNodesAndTokens<TSpecificAnnotation>(SyntaxNode node) where TSpecificAnnotation : TAnnotation => node.GetAnnotatedNodesAndTokens(annotationKind).Where(this.HasAnnotations<TSpecificAnnotation>); public IEnumerable<SyntaxNode> GetAnnotatedNodes<TSpecificAnnotation>(SyntaxNode node) where TSpecificAnnotation : TAnnotation => node.GetAnnotatedNodesAndTokens(annotationKind).Where(nt => nt.IsNode && this.HasAnnotations<TSpecificAnnotation>(nt)).Select(nt => nt.AsNode()!); public IEnumerable<SyntaxToken> GetAnnotatedTokens<TSpecificAnnotation>(SyntaxNode node) where TSpecificAnnotation : TAnnotation => node.GetAnnotatedNodesAndTokens(annotationKind).Where(nt => nt.IsToken && this.HasAnnotations<TSpecificAnnotation>(nt)).Select(nt => nt.AsToken()); public IEnumerable<SyntaxTrivia> GetAnnotatedTrivia<TSpecificAnnotation>(SyntaxNode node) where TSpecificAnnotation : TAnnotation => node.GetAnnotatedTrivia(annotationKind).Where(this.HasAnnotations<TSpecificAnnotation>); } |