File: CSharpUseNullPropagationDiagnosticAnalyzer.cs
Web Access
Project: ..\..\..\src\CodeStyle\CSharp\Analyzers\Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle)
// 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.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.UseNullPropagation;
 
namespace Microsoft.CodeAnalysis.CSharp.UseNullPropagation
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    internal class CSharpUseNullPropagationDiagnosticAnalyzer :
        AbstractUseNullPropagationDiagnosticAnalyzer<
            SyntaxKind,
            ExpressionSyntax,
            StatementSyntax,
            ConditionalExpressionSyntax,
            BinaryExpressionSyntax,
            InvocationExpressionSyntax,
            ConditionalAccessExpressionSyntax,
            ElementAccessExpressionSyntax,
            MemberAccessExpressionSyntax,
            IfStatementSyntax,
            ExpressionStatementSyntax>
    {
        protected override SyntaxKind IfStatementSyntaxKind => SyntaxKind.IfStatement;
 
        protected override bool ShouldAnalyze(Compilation compilation)
            => compilation.LanguageVersion() >= LanguageVersion.CSharp6;
 
        protected override ISyntaxFacts GetSyntaxFacts()
            => CSharpSyntaxFacts.Instance;
 
        protected override bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol? expressionTypeOpt, CancellationToken cancellationToken)
            => node.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken);
 
        protected override bool TryAnalyzePatternCondition(
            ISyntaxFacts syntaxFacts, ExpressionSyntax conditionNode,
            [NotNullWhen(true)] out ExpressionSyntax? conditionPartToCheck, out bool isEquals)
        {
            conditionPartToCheck = null;
            isEquals = true;
 
            if (conditionNode is not IsPatternExpressionSyntax patternExpression)
                return false;
 
            var pattern = patternExpression.Pattern;
            if (pattern is UnaryPatternSyntax(SyntaxKind.NotPattern) notPattern)
            {
                isEquals = false;
                pattern = notPattern.Pattern;
            }
 
            if (pattern is not ConstantPatternSyntax constantPattern)
                return false;
 
            if (!syntaxFacts.IsNullLiteralExpression(constantPattern.Expression))
                return false;
 
            conditionPartToCheck = patternExpression.Expression;
            return true;
        }
 
        protected override bool TryGetPartsOfIfStatement(
            IfStatementSyntax ifStatement,
            [NotNullWhen(true)] out ExpressionSyntax? condition,
            [NotNullWhen(true)] out StatementSyntax? trueStatement)
        {
            // has to be of the form:
            //
            //   if (...)
            //      statement
            //
            // or
            //
            //   if (...)
            //   {
            //       statement
            //   }
 
            condition = ifStatement.Condition;
 
            trueStatement = null;
            if (ifStatement.Else == null)
            {
                if (ifStatement.Statement is BlockSyntax block)
                {
                    if (block.Statements.Count == 1)
                        trueStatement = block.Statements[0];
                }
                else
                {
                    trueStatement = ifStatement.Statement;
                }
            }
 
            return trueStatement != null;
        }
    }
}