File: NullableAttributesVisitor.cs
Web Access
Project: src\src\Compilers\Test\Utilities\CSharp\Microsoft.CodeAnalysis.CSharp.Test.Utilities.csproj (Microsoft.CodeAnalysis.CSharp.Test.Utilities)
// 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.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities
{
    /// <summary>
    /// Returns a string with all symbols containing nullable attributes.
    /// </summary>
    internal sealed class NullableAttributesVisitor : TestAttributesVisitor
    {
        internal static string GetString(PEModuleSymbol module)
        {
            var builder = new StringBuilder();
            var visitor = new NullableAttributesVisitor(module, builder);
            visitor.Visit(module);
            return builder.ToString();
        }
 
        private readonly PEModuleSymbol _module;
        private CSharpAttributeData? _nullableContext;
 
        private NullableAttributesVisitor(PEModuleSymbol module, StringBuilder builder) : base(builder)
        {
            _module = module;
        }
 
        public override void VisitNamedType(NamedTypeSymbol type)
        {
            var previousContext = _nullableContext;
            _nullableContext = GetNullableContextAttribute(type.GetAttributes()) ?? _nullableContext;
 
            base.VisitNamedType(type);
 
            _nullableContext = previousContext;
        }
 
        public override void VisitMethod(MethodSymbol method)
        {
            var previousContext = _nullableContext;
            _nullableContext = GetNullableContextAttribute(method.GetAttributes()) ?? _nullableContext;
 
            base.VisitMethod(method);
 
            _nullableContext = previousContext;
        }
 
        protected override SymbolDisplayFormat DisplayFormat => SymbolDisplayFormat.TestFormatWithConstraints.
            WithMemberOptions(
                SymbolDisplayMemberOptions.IncludeParameters |
                SymbolDisplayMemberOptions.IncludeType |
                SymbolDisplayMemberOptions.IncludeRef |
                SymbolDisplayMemberOptions.IncludeExplicitInterface);
 
        protected override void ReportSymbol(Symbol symbol)
        {
            var nullableContextAttribute = GetNullableContextAttribute(symbol.GetAttributes());
            var nullableAttribute = GetNullableAttribute((symbol is MethodSymbol method) ? method.GetReturnTypeAttributes() : symbol.GetAttributes());
 
            if (nullableContextAttribute == null && nullableAttribute == null)
            {
                if (_nullableContext == null)
                {
                    // No explicit attributes on this symbol or above.
                    return;
                }
                // No explicit attributes on this symbol. Check if nullability metadata was dropped.
                if (!_module.ShouldDecodeNullableAttributes(GetAccessSymbol(symbol)))
                {
                    return;
                }
            }
 
            ReportContainingSymbols(symbol);
            _builder.Append(GetIndentString(symbol));
 
            if (nullableContextAttribute != null)
            {
                _builder.Append($"{ReportAttribute(nullableContextAttribute)} ");
            }
 
            if (nullableAttribute != null)
            {
                _builder.Append($"{ReportAttribute(nullableAttribute)} ");
            }
 
            _builder.AppendLine(symbol.ToDisplayString(DisplayFormat));
            _reported.Add(symbol);
        }
 
        private static Symbol GetAccessSymbol(Symbol symbol)
        {
            while (true)
            {
                switch (symbol.Kind)
                {
                    case SymbolKind.Parameter:
                    case SymbolKind.TypeParameter:
                        symbol = symbol.ContainingSymbol;
                        break;
                    default:
                        return symbol;
                }
            }
        }
 
        private static CSharpAttributeData? GetNullableContextAttribute(ImmutableArray<CSharpAttributeData> attributes) =>
            GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableContextAttribute");
 
        private static CSharpAttributeData? GetNullableAttribute(ImmutableArray<CSharpAttributeData> attributes) =>
            GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableAttribute");
 
        protected override bool TypeRequiresAttribute(TypeSymbol? type)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        protected override CSharpAttributeData GetTargetAttribute(ImmutableArray<CSharpAttributeData> attributes)
        {
            throw ExceptionUtilities.Unreachable();
        }
    }
}