File: Microsoft.NetCore.Analyzers\Usage\CSharpMissingShebangInFileBasedProgram.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.CSharp.NetAnalyzers\Microsoft.CodeAnalysis.CSharp.NetAnalyzers.csproj (Microsoft.CodeAnalysis.CSharp.NetAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.NetCore.Analyzers.Usage;

namespace Microsoft.NetCore.CSharp.Analyzers.Usage
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class CSharpMissingShebangInFileBasedProgram : MissingShebangInFileBasedProgram
    {
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(context =>
            {
                var entryPointFilePath = context.Options.GetMSBuildPropertyValue(
                    MSBuildPropertyOptionNames.EntryPointFilePath, context.Compilation);
                if (string.IsNullOrEmpty(entryPointFilePath))
                {
                    return;
                }

                context.RegisterSyntaxTreeAction(context =>
                {
                    if (!context.Tree.FilePath.Equals(entryPointFilePath, StringComparison.Ordinal))
                    {
                        return;
                    }

                    var root = context.Tree.GetRoot(context.CancellationToken);
                    if (root.GetLeadingTrivia().Any(SyntaxKind.ShebangDirectiveTrivia))
                    {
                        return;
                    }

                    var shebangRequiringDirective = root.GetLeadingTrivia().FirstOrDefault(IsShebangRequiringDirective);
                    if (shebangRequiringDirective == default)
                    {
                        return;
                    }

                    var location = shebangRequiringDirective.GetLocation();
                    context.ReportDiagnostic(location.CreateDiagnostic(Rule));
                });
            });
        }

        private static bool IsShebangRequiringDirective(SyntaxTrivia trivia)
        {
            var structure = trivia.GetStructure();
            if (structure is null)
            {
                return false;
            }

            var content = structure.ChildTokens().FirstOrDefault(static token => token.IsKind(SyntaxKind.StringLiteralToken));
            if (!content.IsKind(SyntaxKind.StringLiteralToken))
            {
                return false;
            }

            var trimmedContent = content.Text.AsSpan().TrimStart();
            return isDirective(trimmedContent, "include") ||
                isDirective(trimmedContent, "ref");

            static bool isDirective(ReadOnlySpan<char> content, string directiveName)
            {
                return content.StartsWith(directiveName, StringComparison.Ordinal) &&
                    // `content` is either exactly the directive name or has other text separated from the directive name by whitespace.
                    (content.Length == directiveName.Length || char.IsWhiteSpace(content[directiveName.Length]));
            }
        }
    }
}