| File: Parsing\Parser.TagProvider.cs | Web Access |
| Project: src\src\Generators\Microsoft.Gen.Logging\Microsoft.Gen.Logging.csproj (Microsoft.Gen.Logging) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.Gen.Logging.Model; using Microsoft.Gen.Shared; namespace Microsoft.Gen.Logging.Parsing; internal partial class Parser { private bool ProcessTagProviderForParameter( AttributeData tagProviderAttribute, LoggingMethodParameter lp, IParameterSymbol paramSymbol, SymbolHolder symbols) { var paramName = paramSymbol.Name; if (!lp.IsNormalParameter) { Diag(DiagDescriptors.TagProviderInvalidUsage, paramSymbol.GetLocation(), paramName); return false; } var paramTypeSymbol = paramSymbol.Type; (lp.OmitReferenceName, var providerType, var providerMethodName) = AttributeProcessors.ExtractTagProviderAttributeValues(tagProviderAttribute); var providerMethod = ValidateTagProvider( providerType, providerMethodName, symbols.ITagCollectorSymbol, paramTypeSymbol, tagProviderAttribute.ApplicationSyntaxReference!.GetSyntax(_cancellationToken).GetLocation()); if (providerMethod is not null) { lp.TagProvider = new(providerMethod.Name, providerType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); return true; } return false; } private bool ProcessTagProviderForProperty( AttributeData tagProviderAttribute, LoggingProperty lp, IPropertySymbol propSymbol, SymbolHolder symbols) { var propName = propSymbol.Name; var propTypeSymbol = propSymbol.Type; (lp.OmitReferenceName, var providerType, var providerMethodName) = AttributeProcessors.ExtractTagProviderAttributeValues(tagProviderAttribute); var providerMethod = ValidateTagProvider( providerType, providerMethodName, symbols.ITagCollectorSymbol, propTypeSymbol, tagProviderAttribute.ApplicationSyntaxReference!.GetSyntax(_cancellationToken).GetLocation()); if (providerMethod is not null) { lp.TagProvider = new(providerMethod.Name, providerType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); return true; } return false; } private IMethodSymbol? ValidateTagProvider( ITypeSymbol providerType, string? providerMethodName, ITypeSymbol tagCollectorType, ITypeSymbol complexObjType, Location? attrLocation) { if (providerType is IErrorTypeSymbol) { return null; } if (providerMethodName != null) { var methodSymbols = providerType.GetMembers(providerMethodName).Where(m => m.Kind == SymbolKind.Method).Cast<IMethodSymbol>(); bool visitedLoop = false; foreach (var method in methodSymbols) { visitedLoop = true; #pragma warning disable S1067 // Expressions should not be too complex if (method.IsStatic && method.ReturnsVoid && !method.IsGenericMethod && IsParameterCountValid(method) && method.Parameters[0].RefKind == RefKind.None && method.Parameters[1].RefKind == RefKind.None && SymbolEqualityComparer.Default.Equals(tagCollectorType, method.Parameters[0].Type) && IsAssignableTo(complexObjType, method.Parameters[1].Type)) #pragma warning restore S1067 // Expressions should not be too complex { if (IsProviderMethodVisible(method)) { return method; } Diag(DiagDescriptors.TagProviderMethodInaccessible, attrLocation, providerMethodName, providerType.ToString()); return null; } } if (visitedLoop) { Diag(DiagDescriptors.TagProviderMethodInvalidSignature, attrLocation, providerMethodName, providerType.ToString(), $"static void {providerMethodName}(ITagCollector, {complexObjType.Name})"); return null; } } Diag(DiagDescriptors.TagProviderMethodNotFound, attrLocation, providerMethodName, providerType.ToString()); return null; static bool IsParameterCountValid(IMethodSymbol method) { if (method.Parameters.Length == 2) { return true; } if (method.Parameters.Length < 2) { return false; } for (int i = 2; i < method.Parameters.Length; i++) { if (!method.Parameters[i].IsOptional) { return false; } } return true; } bool IsAssignableTo(ITypeSymbol type, ITypeSymbol target) { if (type.NullableAnnotation == NullableAnnotation.Annotated) { if (target.NullableAnnotation == NullableAnnotation.NotAnnotated) { return false; } } if (target.TypeKind == TypeKind.Interface) { if (SymbolEqualityComparer.Default.Equals(type.WithNullableAnnotation(NullableAnnotation.None), target.WithNullableAnnotation(NullableAnnotation.None))) { return true; } foreach (var iface in type.AllInterfaces) { if (SymbolEqualityComparer.Default.Equals(target.WithNullableAnnotation(NullableAnnotation.None), iface.WithNullableAnnotation(NullableAnnotation.None))) { return true; } } return false; } return ParserUtilities.IsBaseOrIdentity(type, target, _compilation); } static bool IsProviderMethodVisible(ISymbol symbol) { while (symbol != null && symbol.Kind != SymbolKind.Namespace) { switch (symbol.DeclaredAccessibility) { case Accessibility.NotApplicable: case Accessibility.Private: case Accessibility.Protected: return false; } symbol = symbol.ContainingSymbol; } return true; } } }