Skip to content

Commit

Permalink
Backport extensions for new generators
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Dec 18, 2024
1 parent 418a2e8 commit 1f63560
Show file tree
Hide file tree
Showing 18 changed files with 276 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public static bool TryGetAccessibilityModifiers(
}

// Track the accessors accessibility, if explicitly set
foreach (AccessorDeclarationSyntax accessor in node.AccessorList?.Accessors ?? [])
foreach (AccessorDeclarationSyntax accessor in node.AccessorList?.Accessors ?? default)
{
if (accessor.Modifiers.Count == 0)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
#if WINDOWS_UWP
using ComputeSharp.D2D1.Uwp.SourceGenerators.Constants;
using ComputeSharp.D2D1.Uwp.SourceGenerators.Models;
Expand Down Expand Up @@ -136,8 +137,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
item.Hierarchy.WriteSyntax(
state: item.Properties,
writer: writer,
baseTypes: [],
memberCallbacks: [Execute.WritePropertyDeclarations]);
baseTypes: ReadOnlySpan<string>.Empty,
memberCallbacks: new IndentedTextWriter.Callback<EquatableArray<CanvasEffectPropertyInfo>>[] { Execute.WritePropertyDeclarations });

context.AddSource($"{item.Hierarchy.FullyQualifiedMetadataName}.g.cs", writer.ToString());
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace ComputeSharp.D2D1.WinUI.SourceGenerators;
public sealed class InvalidGeneratedCanvasEffectPropertyAccessorsAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [InvalidGeneratedCanvasEffectPropertyAccessors];
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(InvalidGeneratedCanvasEffectPropertyAccessors);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace ComputeSharp.D2D1.WinUI.SourceGenerators;
public sealed class InvalidGeneratedCanvasEffectPropertyContainingTypeAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [InvalidGeneratedCanvasEffectPropertyContainingType];
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(InvalidGeneratedCanvasEffectPropertyContainingType);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ namespace ComputeSharp.D2D1.WinUI.SourceGenerators;
public sealed class InvalidGeneratedCanvasEffectPropertyDeclarationAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
[
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
InvalidGeneratedCanvasEffectPropertyDeclarationIsStatic,
InvalidGeneratedCanvasEffectPropertyDeclarationIsNotIncompletePartialDefinition,
InvalidGeneratedCanvasEffectPropertyDeclarationReturnsByRef,
InvalidGeneratedCanvasEffectPropertyDeclarationReturnsRefLikeType
];
InvalidGeneratedCanvasEffectPropertyDeclarationReturnsRefLikeType);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace ComputeSharp.D2D1.WinUI.SourceGenerators;
public sealed class RequireCSharpLanguageVersionPreviewAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [CSharpLanguageVersionIsNotPreview];
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(CSharpLanguageVersionIsNotPreview);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
Expand Down
2 changes: 2 additions & 0 deletions src/ComputeSharp.D2D1.UI/CanvasEffectInvalidationType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#if !D2D1_UI_SOURCE_GENERATOR
using Microsoft.Graphics.Canvas;
#endif

#if WINDOWS_UWP
namespace ComputeSharp.D2D1.Uwp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,9 @@

namespace ComputeSharp.SourceGeneration.Extensions;

/// <summary>
/// A <see langword="class"/> with some extension methods for C# syntax nodes.
/// </summary>
internal static class SyntaxNodeExtensions
/// <inheritdoc/>
internal static partial class SyntaxNodeExtensions
{
/// <summary>
/// Checks whether a given <see cref="SyntaxNode"/> is a given type declaration with or potentially with any base types, using only syntax.
/// </summary>
/// <typeparam name="T">The type of declaration to check for.</typeparam>
/// <param name="node">The input <see cref="SyntaxNode"/> to check.</param>
/// <returns>Whether <paramref name="node"/> is a given type declaration with or potentially with any base types.</returns>
public static bool IsTypeDeclarationWithOrPotentiallyWithBaseTypes<T>(this SyntaxNode node)
where T : TypeDeclarationSyntax
{
// Immediately bail if the node is not a type declaration of the specified type
if (node is not T typeDeclaration)
{
return false;
}

// If the base types list is not empty, the type can definitely has implemented interfaces
if (typeDeclaration.BaseList is { Types.Count: > 0 })
{
return true;
}

// If the base types list is empty, check if the type is partial. If it is, it means
// that there could be another partial declaration with a non-empty base types list.
return typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword);
}

/// <summary>
/// Checks a <see cref="SyntaxNode"/> value and replaces the value type to be HLSL compatible, if needed.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@
<Import_RootNamespace>ComputeSharp.SourceGeneration</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Constants\WellKnownTrackingNames.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AccessibilityExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AttributeDataExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\CompilationExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\DiagnosticsExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IMethodSymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalValuesProviderExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IndentedTextWriterExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IPropertySymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IFieldSymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ITypeSymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ISymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SymbolInfoExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SyntaxNodeExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)helpers\DynamicCache{TKey,TValue}.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\EquatableArray{T}.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\HashCode.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace ComputeSharp.SourceGeneration.Constants;

/// <summary>
/// The well known names for tracking steps, to test the incremental generators.
/// </summary>
internal static class WellKnownTrackingNames
{
/// <summary>
/// The initial <see cref="Microsoft.CodeAnalysis.SyntaxValueProvider.ForAttributeWithMetadataName"/> transform node.
/// </summary>
public const string Execute = nameof(Execute);

/// <summary>
/// The filtered transform with just output diagnostics.
/// </summary>
public const string Diagnostics = nameof(Diagnostics);

/// <summary>
/// The filtered transform with just output sources.
/// </summary>
public const string Output = nameof(Output);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.CodeAnalysis;

namespace ComputeSharp.SourceGeneration.Extensions;

/// <summary>
/// Extension methods for the <see cref="Accessibility"/> type.
/// </summary>
internal static class AccessibilityExtensions
{
/// <summary>
/// Gets the expression for a given <see cref="Accessibility"/> value.
/// </summary>
/// <param name="accessibility">The input <see cref="Accessibility"/> value.</param>
/// <returns>The expression for <paramref name="accessibility"/>.</returns>
public static string GetExpression(this Accessibility accessibility)
{
return accessibility switch
{
Accessibility.Private => "private",
Accessibility.ProtectedAndInternal => "private protected",
Accessibility.Protected => "protected",
Accessibility.Internal => "internal",
Accessibility.ProtectedOrInternal => "protected internal",
Accessibility.Public => "public",
_ => ""
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ public static bool TryBuildNamedTypeSymbolSet(
return true;
}

/// <summary>
/// Checks whether a given compilation (assumed to be for C#) is using the preview language version.
/// </summary>
/// <param name="compilation">The <see cref="Compilation"/> to consider for analysis.</param>
/// <returns>Whether <paramref name="compilation"/> is using the preview language version.</returns>
public static bool IsLanguageVersionPreview(this Compilation compilation)
{
return ((CSharpCompilation)compilation).LanguageVersion == LanguageVersion.Preview;
}

/// <summary>
/// Checks whether the <c>AllowUnsafeBlocks</c> option is set for a given compilation.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/ComputeSharp.SourceGeneration/Extensions/ISymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ public static string GetFullyQualifiedName(this ISymbol symbol)
return symbol.ToDisplayString(FullyQualifiedWithoutGlobalFormat);
}

/// <summary>
/// Gets the fully qualified name for a given symbol, including nullability annotations
/// </summary>
/// <param name="symbol">The input <see cref="ISymbol"/> instance.</param>
/// <returns>The fully qualified name for <paramref name="symbol"/>.</returns>
public static string GetFullyQualifiedNameWithNullabilityAnnotations(this ISymbol symbol)
{
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier));
}

/// <summary>
/// Checks whether or not a given symbol has an attribute with the specified type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,52 @@ public static bool HasFullyQualifiedMetadataName(this ITypeSymbol symbol, string
return builder.WrittenSpan.SequenceEqual(name.AsSpan());
}

/// <summary>
/// Checks whether or not a given <see cref="ITypeSymbol"/> inherits from a specified type.
/// </summary>
/// <param name="typeSymbol">The target <see cref="ITypeSymbol"/> instance to check.</param>
/// <param name="name">The full name of the type to check for inheritance.</param>
/// <returns>Whether or not <paramref name="typeSymbol"/> inherits from <paramref name="name"/>.</returns>
public static bool InheritsFromFullyQualifiedMetadataName(this ITypeSymbol typeSymbol, string name)
{
INamedTypeSymbol? baseType = typeSymbol.BaseType;

while (baseType is not null)
{
if (baseType.HasFullyQualifiedMetadataName(name))
{
return true;
}

baseType = baseType.BaseType;
}

return false;
}

/// <summary>
/// Checks whether or not a given <see cref="ITypeSymbol"/> inherits from a specified type.
/// </summary>
/// <param name="typeSymbol">The target <see cref="ITypeSymbol"/> instance to check.</param>
/// <param name="baseTypeSymbol">The <see cref="ITypeSymbol"/> instane to check for inheritance from.</param>
/// <returns>Whether or not <paramref name="typeSymbol"/> inherits from <paramref name="baseTypeSymbol"/>.</returns>
public static bool InheritsFromType(this ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol)
{
INamedTypeSymbol? currentBaseTypeSymbol = typeSymbol.BaseType;

while (currentBaseTypeSymbol is not null)
{
if (SymbolEqualityComparer.Default.Equals(currentBaseTypeSymbol, baseTypeSymbol))
{
return true;
}

currentBaseTypeSymbol = currentBaseTypeSymbol.BaseType;
}

return false;
}

/// <summary>
/// Checks whether or not a given <see cref="ITypeSymbol"/> implements an interface of a specified type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using ComputeSharp.SourceGeneration.Helpers;
using Microsoft.CodeAnalysis;

namespace ComputeSharp.SourceGeneration.Extensions;

/// <summary>
/// Extension methods for <see cref="IncrementalValuesProvider{TValues}"/>.
/// </summary>
internal static class IncrementalValuesProviderExtensions
{
/// <summary>
/// Groups items in a given <see cref="IncrementalValuesProvider{TValue}"/> sequence by a specified key.
/// </summary>
/// <typeparam name="TValues">The type of value that this source provides access to.</typeparam>
/// <typeparam name="TKey">The type of resulting key elements.</typeparam>
/// <typeparam name="TElement">The type of resulting projected elements.</typeparam>
/// <param name="source">The input <see cref="IncrementalValuesProvider{TValues}"/> instance.</param>
/// <param name="keySelector">The key selection <see cref="Func{T, TResult}"/>.</param>
/// <param name="elementSelector">The element selection <see cref="Func{T, TResult}"/>.</param>
/// <returns>An <see cref="IncrementalValuesProvider{TValues}"/> with the grouped results.</returns>
public static IncrementalValuesProvider<(TKey Key, EquatableArray<TElement> Right)> GroupBy<TValues, TKey, TElement>(
this IncrementalValuesProvider<TValues> source,
Func<TValues, TKey> keySelector,
Func<TValues, TElement> elementSelector)
where TValues : IEquatable<TValues>
where TKey : IEquatable<TKey>
where TElement : IEquatable<TElement>
{
return source.Collect().SelectMany((item, token) =>
{
Dictionary<TKey, ImmutableArray<TElement>.Builder> map = new();

foreach (TValues value in item)
{
TKey key = keySelector(value);
TElement element = elementSelector(value);

if (!map.TryGetValue(key, out ImmutableArray<TElement>.Builder builder))
{
builder = ImmutableArray.CreateBuilder<TElement>();

map.Add(key, builder);
}

builder.Add(element);
}

token.ThrowIfCancellationRequested();

ImmutableArray<(TKey Key, EquatableArray<TElement> Elements)>.Builder result =
ImmutableArray.CreateBuilder<(TKey, EquatableArray<TElement>)>();

foreach (KeyValuePair<TKey, ImmutableArray<TElement>.Builder> entry in map)
{
result.Add((entry.Key, entry.Value.ToImmutable()));
}

return result;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ internal static class IndentedTextWriterExtensions
/// <param name="writer">The <see cref="IndentedTextWriter"/> instance to write into.</param>
/// <param name="generatorName">The name of the generator.</param>
/// <param name="useFullyQualifiedTypeNames">Whether to use fully qualified type names or not.</param>
public static void WriteGeneratedAttributes(this IndentedTextWriter writer, string generatorName, bool useFullyQualifiedTypeNames = true)
/// <param name="includeNonUserCodeAttributes">Whether to also include the attribute for non-user code.</param>
public static void WriteGeneratedAttributes(
this IndentedTextWriter writer,
string generatorName,
bool useFullyQualifiedTypeNames = true,
bool includeNonUserCodeAttributes = true)
{
// We can use this class to get the assembly, as all files for generators are just included
// via shared projects. As such, the assembly will be the same as the generator type itself.
Expand All @@ -28,14 +33,22 @@ public static void WriteGeneratedAttributes(this IndentedTextWriter writer, stri
if (useFullyQualifiedTypeNames)
{
writer.WriteLine($$"""[global::System.CodeDom.Compiler.GeneratedCode("{{generatorName}}", "{{assemblyVersion}}")]""");
writer.WriteLine($$"""[global::System.Diagnostics.DebuggerNonUserCode]""");
writer.WriteLine($$"""[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]""");

if (includeNonUserCodeAttributes)
{
writer.WriteLine($$"""[global::System.Diagnostics.DebuggerNonUserCode]""");
writer.WriteLine($$"""[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]""");
}
}
else
{
writer.WriteLine($$"""[GeneratedCode("{{generatorName}}", "{{assemblyVersion}}")]""");
writer.WriteLine($$"""[DebuggerNonUserCode]""");
writer.WriteLine($$"""[ExcludeFromCodeCoverage]""");

if (includeNonUserCodeAttributes)
{
writer.WriteLine($$"""[DebuggerNonUserCode]""");
writer.WriteLine($$"""[ExcludeFromCodeCoverage]""");
}
}
}

Expand Down
Loading

0 comments on commit 1f63560

Please sign in to comment.