/// <summary> /// Creates a syntax provider that looks for syntax nodes with a given attribute. /// </summary> /// <typeparam name="T">The exact syntax node type to search for.</typeparam> /// <param name="syntaxValueProvider">The syntax value provider to register on.</param> /// <param name="attributeType">The attribute type to search for.</param> /// <returns>The values provider with the syntax nodes attributed with an attribute of type /// <paramref name="attributeType"/>.</returns> public static IncrementalValuesProvider <T> CreateAttributedSyntaxProvider <T>( this SyntaxValueProvider syntaxValueProvider, Type attributeType) where T : MemberDeclarationSyntax => syntaxValueProvider.CreateSyntaxProvider( // Filter for type declarations with at least one attribute predicate : (n, _) => IsSyntaxTargetForGeneration <T>(n), // Transform to either the semantic value or null, if not the right attribute transform : (ctx, ct) => GetSemanticTargetForGeneration <T>(ctx, attributeType.FullName, ct)) // Discard null .Where(m => m is not null) !;
/// <summary> /// Creates an <see cref="IncrementalValuesProvider{T}"/> that can provide a transform over all <see /// cref="SyntaxNode"/>s if that node has an attribute on it that binds to a <see cref="INamedTypeSymbol"/> with the /// same fully-qualified metadata as the provided <paramref name="fullyQualifiedMetadataName"/>. <paramref /// name="fullyQualifiedMetadataName"/> should be the fully-qualified, metadata name of the attribute, including the /// <c>Attribute</c> suffix. For example <c>"System.CLSCompliantAttribute</c> for <see /// cref="System.CLSCompliantAttribute"/>. /// </summary> /// <param name="predicate">A function that determines if the given <see cref="SyntaxNode"/> attribute target (<see /// cref="GeneratorAttributeSyntaxContext.TargetNode"/>) should be transformed. Nodes that do not pass this /// predicate will not have their attributes looked at at all.</param> /// <param name="transform">A function that performs the transform. This will only be passed nodes that return <see /// langword="true"/> for <paramref name="predicate"/> and which have a matching <see cref="AttributeData"/> whose /// <see cref="AttributeData.AttributeClass"/> has the same fully qualified, metadata name as <paramref /// name="fullyQualifiedMetadataName"/>.</param> public static IncrementalValuesProvider <T> ForAttributeWithMetadataName <T>( this SyntaxValueProvider @this, IncrementalGeneratorInitializationContext context, string fullyQualifiedMetadataName, Func <SyntaxNode, CancellationToken, bool> predicate, Func <GeneratorAttributeSyntaxContext, CancellationToken, T> transform) { #if false // Deviation from roslyn. We do not support attributes that are nested or generic. That's ok as that's not a // scenario that ever arises in our generators. var metadataName = fullyQualifiedMetadataName.Contains('+') ? MetadataTypeName.FromFullName(fullyQualifiedMetadataName.Split(s_nestedTypeNameSeparators).Last()) : MetadataTypeName.FromFullName(fullyQualifiedMetadataName); var nodesWithAttributesMatchingSimpleName = @this.ForAttributeWithSimpleName(context, metadataName.UnmangledTypeName, predicate); #else var lastDotIndex = fullyQualifiedMetadataName.LastIndexOf('.'); Debug.Assert(lastDotIndex > 0); var unmangledTypeName = fullyQualifiedMetadataName.Substring(lastDotIndex + 1); var nodesWithAttributesMatchingSimpleName = @this.ForAttributeWithSimpleName(context, unmangledTypeName, predicate); #endif var compilationAndGroupedNodesProvider = nodesWithAttributesMatchingSimpleName .Combine(context.CompilationProvider) /*.WithTrackingName("compilationAndGroupedNodes_ForAttributeWithMetadataName")*/; var syntaxHelper = CSharpSyntaxHelper.Instance; var finalProvider = compilationAndGroupedNodesProvider.SelectMany((tuple, cancellationToken) => { var((syntaxTree, syntaxNodes), compilation) = tuple; Debug.Assert(syntaxNodes.All(n => n.SyntaxTree == syntaxTree)); using var result = new ValueListBuilder <T>(Span <T> .Empty); if (!syntaxNodes.IsEmpty) { var semanticModel = compilation.GetSemanticModel(syntaxTree); foreach (var targetNode in syntaxNodes) { cancellationToken.ThrowIfCancellationRequested(); var targetSymbol = targetNode is ICompilationUnitSyntax compilationUnit ? semanticModel.Compilation.Assembly : syntaxHelper.IsLambdaExpression(targetNode) ? semanticModel.GetSymbolInfo(targetNode, cancellationToken).Symbol : semanticModel.GetDeclaredSymbol(targetNode, cancellationToken); if (targetSymbol is null) { continue; } var attributes = getMatchingAttributes(targetNode, targetSymbol, fullyQualifiedMetadataName); if (attributes.Length > 0) { result.Append(transform( new GeneratorAttributeSyntaxContext(targetNode, targetSymbol, semanticModel, attributes), cancellationToken)); } } } return(result.AsSpan().ToImmutableArray()); }) /*.WithTrackingName("result_ForAttributeWithMetadataName")*/; return(finalProvider);