// 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.IO; using System.Linq; using System.Reflection; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; namespace CommunityToolkit.Mvvm.SourceGenerators; /// /// A source generator for necessary nullability attributes. /// [Generator] public sealed class NullabilityAttributesGenerator : ISourceGenerator { /// public void Initialize(GeneratorInitializationContext context) { } /// public void Execute(GeneratorExecutionContext context) { AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullAttribute"); AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute"); } /// /// Adds the source for a given attribute type if it's not present already in the compilation. /// private void AddSourceCodeIfTypeIsNotPresent(GeneratorExecutionContext context, string typeFullName) { // Check that the target attributes are not available in the consuming project. To ensure that // this works fine both in .NET (Core) and .NET Standard implementations, we also need to check // that the target types are declared as public (we assume that in this case those types are from the BCL). // This avoids issues on .NET Standard with Roslyn also seeing internal types from referenced assemblies. if (context.Compilation.GetTypeByMetadataName(typeFullName) is { DeclaredAccessibility: Accessibility.Public }) { return; } string typeName = typeFullName.Split('.').Last(); string filename = $"CommunityToolkit.Mvvm.SourceGenerators.EmbeddedResources.{typeName}.cs"; Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename); StreamReader reader = new(stream); string source = reader.ReadToEnd(); context.AddSource($"{typeFullName}.cs", SourceText.From(source, Encoding.UTF8)); } }