Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
Expand All @@ -15,50 +14,31 @@ public class ContextualOptionsGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValuesProvider<SyntaxNode> typeDeclarations = context.SyntaxProvider
IncrementalValuesProvider<OptionsContextType> types = context.SyntaxProvider
.ForAttributeWithMetadataName(
"Microsoft.Extensions.Options.Contextual.OptionsContextAttribute",
(_, _) => true,
(context, _) => context.TargetNode);
(node, _) => node is TypeDeclarationSyntax,
(context, _) => CreateOptionsContextType(context));

IncrementalValueProvider<(Compilation, ImmutableArray<SyntaxNode>)> compilationAndTypes =
context.CompilationProvider.Combine(typeDeclarations.Collect());
IncrementalValueProvider<(Compilation, ImmutableArray<OptionsContextType>)> compilationAndTypes =
context.CompilationProvider.Combine(types.Collect());

context.RegisterSourceOutput(compilationAndTypes, static (spc, source) => HandleAnnotatedTypes(source.Item1, source.Item2, spc));
context.RegisterSourceOutput(types.Collect(), static (spc, source) => HandleAnnotatedTypes(source, spc));
}

private static void HandleAnnotatedTypes(Compilation compilation, IEnumerable<SyntaxNode> nodes, SourceProductionContext context)
private static OptionsContextType CreateOptionsContextType(GeneratorAttributeSyntaxContext context)
{
if (!SymbolLoader.TryLoad(compilation, out var holder))
{
return;
}

var typeDeclarations = nodes.OfType<TypeDeclarationSyntax>()
.ToLookup(declaration => declaration.SyntaxTree)
.SelectMany(declarations => declarations.Select(declaration => (symbol: compilation.GetSemanticModel(declarations.Key).GetDeclaredSymbol(declaration), declaration)))
.Where(_ => _.symbol is INamedTypeSymbol)
.Where(_ => _.symbol!.GetAttributes().Any(attribute => SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, holder!.OptionsContextAttribute)))
.ToLookup(_ => _.symbol, _ => _.declaration, comparer: SymbolEqualityComparer.Default)
.ToDictionary<IGrouping<ISymbol?, TypeDeclarationSyntax>, INamedTypeSymbol, List<TypeDeclarationSyntax>>(
group => (INamedTypeSymbol)group.Key!, group => group.ToList(), comparer: SymbolEqualityComparer.Default);

var list = new List<OptionsContextType>();
foreach (var type in Parser.GetContextualOptionTypes(typeDeclarations))
{
context.CancellationToken.ThrowIfCancellationRequested();
type.Diagnostics.ForEach(context.ReportDiagnostic);

if (type.ShouldEmit)
{
list.Add(type);
}
}
var symbol = (INamedTypeSymbol)context.TargetSymbol;
var node = context.TargetNode;
return Parser.GetContextualOptionTypes(symbol, (TypeDeclarationSyntax)node);
}

if (list.Count > 0)
private static void HandleAnnotatedTypes(ImmutableArray<OptionsContextType> types, SourceProductionContext context)
{
if (types.Length > 0)
{
var emitter = new Emitter();
context.AddSource($"ContextualOptions.g.cs", emitter.Emit(list.OrderBy(x => x.Namespace + "." + x.Name)));
context.AddSource($"ContextualOptions.g.cs", emitter.Emit(types.OrderBy(x => x.Namespace + "." + x.Name)));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.Gen.ContextualOptions.Model;

// TODO: Equality

Check failure on line 10 in src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs#L10

src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs(10,4): error S1135: (NETCORE_ENGINEERING_TELEMETRY=Build) Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
internal sealed class OptionsContextType
{
public readonly List<Diagnostic> Diagnostics = [];
public readonly INamedTypeSymbol Symbol;
public readonly ImmutableArray<TypeDeclarationSyntax> Definitions;
public readonly ImmutableArray<string> OptionsContextProperties;
public string Keyword => Definitions[0].Keyword.Text;
public string? Namespace => Symbol.ContainingNamespace.IsGlobalNamespace ? null : Symbol.ContainingNamespace.ToString();
public string Name => Symbol.Name;

public bool ShouldEmit => Diagnostics.TrueForAll(diag => diag.Severity != DiagnosticSeverity.Error);

public string HintName => $"{Namespace}.{Name}";
public string Keyword;
public string? Namespace;
public string Name;

public OptionsContextType(
INamedTypeSymbol symbol,
ImmutableArray<TypeDeclarationSyntax> definitions,
TypeDeclarationSyntax typeDeclarationSyntax,
ImmutableArray<string> optionsContextProperties)
{
Symbol = symbol;
Definitions = definitions;
// NOTE: NEVER store INamedTypeSymbol in OptionsContextType.
// This is better for source generator incrementality.
Name = symbol.Name;
Namespace = symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToString();
Keyword = typeDeclarationSyntax.Keyword.Text;
OptionsContextProperties = optionsContextProperties;
}
}
99 changes: 48 additions & 51 deletions src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Gen.ContextualOptions.Model;

namespace Microsoft.Gen.ContextualOptions;

internal static class Parser
{
public static IEnumerable<OptionsContextType> GetContextualOptionTypes(Dictionary<INamedTypeSymbol, List<TypeDeclarationSyntax>> types) =>
types
.Select(type => new OptionsContextType(type.Key, type.Value.ToImmutableArray(), GetContextProperties(type.Key)))
.Select(CheckInstantiable)
.Select(CheckPartial)
.Select(CheckRefLikeType)
.Select(CheckHasProperties);
public static OptionsContextType GetContextualOptionTypes(INamedTypeSymbol symbol, TypeDeclarationSyntax typeDeclarationSyntax)
=> new OptionsContextType(symbol, typeDeclarationSyntax, GetContextProperties(symbol));

private static OptionsContextType CheckInstantiable(OptionsContextType type)
{
if (type.Symbol.IsStatic)
{
type.Diagnostics.AddRange(
type.Definitions
.SelectMany(def => def.Modifiers)
.Where(modifier => modifier.IsKind(SyntaxKind.StaticKeyword))
.Select(modifier => Diagnostic.Create(DiagDescriptors.ContextCannotBeStatic, modifier.GetLocation(), type.Name)));
}
// TODO: Separate analyzer for diagnostics.

Check failure on line 17 in src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs#L17

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs(17,8): error S1135: (NETCORE_ENGINEERING_TELEMETRY=Build) Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
//private static OptionsContextType CheckInstantiable(OptionsContextType type)
//{

Check failure on line 19 in src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs#L19

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs(19,5): error S125: (NETCORE_ENGINEERING_TELEMETRY=Build) Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)
// if (type.Symbol.IsStatic)
// {
// type.Diagnostics.AddRange(
// type.Definitions
// .SelectMany(def => def.Modifiers)
// .Where(modifier => modifier.IsKind(SyntaxKind.StaticKeyword))
// .Select(modifier => Diagnostic.Create(DiagDescriptors.ContextCannotBeStatic, modifier.GetLocation(), type.Name)));
// }

return type;
}
// return type;
//}

private static OptionsContextType CheckRefLikeType(OptionsContextType type)
{
if (type.Symbol.IsRefLikeType)
{
type.Diagnostics.AddRange(
type.Definitions
.SelectMany(def => def.Modifiers)
.Where(modifier => modifier.IsKind(SyntaxKind.RefKeyword))
.Select(modifier => Diagnostic.Create(DiagDescriptors.ContextCannotBeRefLike, modifier.GetLocation(), type.Name)));
}
// TODO: Separate analyzer for diagnostics.

Check failure on line 32 in src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs#L32

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs(32,8): error S1135: (NETCORE_ENGINEERING_TELEMETRY=Build) Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
//private static OptionsContextType CheckRefLikeType(OptionsContextType type)
//{
// if (type.Symbol.IsRefLikeType)
// {
// type.Diagnostics.AddRange(
// type.Definitions
// .SelectMany(def => def.Modifiers)
// .Where(modifier => modifier.IsKind(SyntaxKind.RefKeyword))
// .Select(modifier => Diagnostic.Create(DiagDescriptors.ContextCannotBeRefLike, modifier.GetLocation(), type.Name)));
// }

return type;
}
// return type;
//}

private static OptionsContextType CheckPartial(OptionsContextType type)
{
if (!type.Definitions.Any(def => def.Modifiers.Any(static token => token.IsKind(SyntaxKind.PartialKeyword))))
{
type.Diagnostics.AddRange(
type.Definitions.Select(def => Diagnostic.Create(DiagDescriptors.ContextMustBePartial, def.Identifier.GetLocation(), type.Name)));
}
// TODO: Separate analyzer for diagnostics.

Check failure on line 47 in src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs#L47

src/Generators/Microsoft.Gen.ContextualOptions/Parser.cs(47,8): error S1135: (NETCORE_ENGINEERING_TELEMETRY=Build) Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
//private static OptionsContextType CheckPartial(OptionsContextType type)
//{
// if (!type.Definitions.Any(def => def.Modifiers.Any(static token => token.IsKind(SyntaxKind.PartialKeyword))))
// {
// type.Diagnostics.AddRange(
// type.Definitions.Select(def => Diagnostic.Create(DiagDescriptors.ContextMustBePartial, def.Identifier.GetLocation(), type.Name)));
// }

return type;
}
// return type;
//}

private static OptionsContextType CheckHasProperties(OptionsContextType type)
{
if (type.OptionsContextProperties.IsEmpty)
{
type.Diagnostics.AddRange(
type.Definitions.Select(def => Diagnostic.Create(DiagDescriptors.ContextDoesNotHaveValidProperties, def.Identifier.GetLocation(), type.Name)));
}
// TODO: Separate analyzer for diagnostics.
//private static OptionsContextType CheckHasProperties(OptionsContextType type)
//{
// if (type.OptionsContextProperties.IsEmpty)
// {
// type.Diagnostics.AddRange(
// type.Definitions.Select(def => Diagnostic.Create(DiagDescriptors.ContextDoesNotHaveValidProperties, def.Identifier.GetLocation(), type.Name)));
// }

return type;
}
// return type;
//}

private static ImmutableArray<string> GetContextProperties(INamedTypeSymbol symbol)
{
Expand Down
10 changes: 0 additions & 10 deletions src/Generators/Microsoft.Gen.ContextualOptions/SymbolHolder.cs

This file was deleted.

23 changes: 0 additions & 23 deletions src/Generators/Microsoft.Gen.ContextualOptions/SymbolLoader.cs

This file was deleted.

Loading