Beispiel #1
0
        private void ReportDiagnostics(
            SyntaxNodeAnalysisContext syntaxContext,
            StatementSyntax firstStatement,
            IfStatementSyntax ifStatement,
            ExpressionStatementSyntax expressionStatement,
            ReportDiagnostic severity,
            List <Location> additionalLocations,
            string kind)
        {
            var tree = syntaxContext.Node.SyntaxTree;

            var properties = ImmutableDictionary <string, string> .Empty.Add(
                Constants.Kind, kind);

            var previousToken = expressionStatement.GetFirstToken().GetPreviousToken();
            var nextToken     = expressionStatement.GetLastToken().GetNextToken();

            // Fade out the code up to the expression statement.
            syntaxContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryWithSuggestionDescriptor,
                                                             Location.Create(tree, TextSpan.FromBounds(firstStatement.SpanStart, previousToken.Span.End)),
                                                             additionalLocations, properties));

            // Put a diagnostic with the appropriate severity on the expression-statement itself.
            syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                               Descriptor,
                                               expressionStatement.GetLocation(),
                                               severity,
                                               additionalLocations, properties));

            // If the if-statement extends past the expression statement, then fade out the rest.
            if (nextToken.Span.Start < ifStatement.Span.End)
            {
                syntaxContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryWithSuggestionDescriptor,
                                                                 Location.Create(tree, TextSpan.FromBounds(nextToken.Span.Start, ifStatement.Span.End)),
                                                                 additionalLocations, properties));
            }
        }
Beispiel #2
0
        private void Analyze(
            SemanticModelAnalysisContext context,
            JsonLanguageDetector detector,
            SyntaxNode node,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            foreach (var child in node.ChildNodesAndTokens())
            {
                if (child.IsNode)
                {
                    Analyze(context, detector, child.AsNode() !, cancellationToken);
                }
                else
                {
                    var token = child.AsToken();
                    if (_info.IsAnyStringLiteral(token.RawKind))
                    {
                        var tree = detector.TryParseString(token, context.SemanticModel, includeProbableStrings: false, cancellationToken);
                        if (tree != null)
                        {
                            foreach (var diag in tree.Diagnostics)
                            {
                                context.ReportDiagnostic(DiagnosticHelper.Create(
                                                             this.Descriptor,
                                                             Location.Create(context.SemanticModel.SyntaxTree, diag.Span),
                                                             ReportDiagnostic.Warn,
                                                             additionalLocations: null,
                                                             properties: null,
                                                             diag.Message));
                            }
                        }
                    }
                }
            }
        }
 private void AnalyzeToken(
     SemanticModelAnalysisContext context,
     RegexLanguageDetector detector,
     SyntaxToken token,
     CancellationToken cancellationToken)
 {
     if (_info.IsAnyStringLiteral(token.RawKind))
     {
         var tree = detector.TryParseString(token, context.SemanticModel, cancellationToken);
         if (tree != null)
         {
             foreach (var diag in tree.Diagnostics)
             {
                 context.ReportDiagnostic(DiagnosticHelper.Create(
                                              Descriptor,
                                              Location.Create(context.SemanticModel.SyntaxTree, diag.Span),
                                              ReportDiagnostic.Warn,
                                              additionalLocations: null,
                                              properties: null,
                                              diag.Message));
             }
         }
     }
 }
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
        {
            var localFunction = (LocalFunctionStatementSyntax)context.Node;

            if (localFunction.Modifiers.Any(SyntaxKind.StaticKeyword))
            {
                return;
            }

            var syntaxTree = context.Node.SyntaxTree;

            if (!MakeLocalFunctionStaticHelper.IsStaticLocalFunctionSupported(syntaxTree))
            {
                return;
            }

            var cancellationToken = context.CancellationToken;
            var option            = context.Options.GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction, syntaxTree, cancellationToken);

            if (!option.Value)
            {
                return;
            }

            var semanticModel = context.SemanticModel;

            if (MakeLocalFunctionStaticHelper.TryGetCaputuredSymbols(localFunction, semanticModel, out var captures) && captures.Length == 0)
            {
                context.ReportDiagnostic(DiagnosticHelper.Create(
                                             Descriptor,
                                             localFunction.Identifier.GetLocation(),
                                             option.Notification.Severity,
                                             additionalLocations: ImmutableArray.Create(localFunction.GetLocation()),
                                             properties: null));
            }
        }
Beispiel #5
0
        private bool CheckStatementSyntax(
            SyntaxTreeAnalysisContext context,
            ReportDiagnostic severity,
            StatementSyntax statement
            )
        {
            if (!StatementNeedsWrapping(statement))
            {
                return(false);
            }

            var additionalLocations = ImmutableArray.Create(statement.GetLocation());

            context.ReportDiagnostic(
                DiagnosticHelper.Create(
                    this.Descriptor,
                    statement.GetFirstToken().GetLocation(),
                    severity,
                    additionalLocations,
                    properties: null
                    )
                );
            return(true);
        }
Beispiel #6
0
        private void ReportDiagnosticsIfNeeded(NameColonSyntax nameColon, SyntaxNodeAnalysisContext context, AnalyzerOptions options, SyntaxTree syntaxTree, CancellationToken cancellationToken)
        {
            if (!nameColon.Parent.IsKind(SyntaxKind.Argument, out ArgumentSyntax? argument))
            {
                return;
            }

            RoslynDebug.Assert(context.Compilation is object);
            var parseOptions = (CSharpParseOptions)syntaxTree.Options;
            var preference   = options.GetOption(
                CodeStyleOptions2.PreferInferredTupleNames, context.Compilation.Language, syntaxTree, cancellationToken);

            if (!preference.Value ||
                !CSharpInferredMemberNameSimplifier.CanSimplifyTupleElementName(argument, parseOptions))
            {
                return;
            }

            // Create a normal diagnostic
            context.ReportDiagnostic(
                DiagnosticHelper.Create(
                    Descriptor,
                    nameColon.GetLocation(),
                    preference.Notification.Severity,
                    additionalLocations: null,
                    properties: null));

            // Also fade out the part of the name-colon syntax
            RoslynDebug.AssertNotNull(UnnecessaryWithoutSuggestionDescriptor);
            var fadeSpan = TextSpan.FromBounds(nameColon.Name.SpanStart, nameColon.ColonToken.Span.End);

            context.ReportDiagnostic(
                Diagnostic.Create(
                    UnnecessaryWithoutSuggestionDescriptor,
                    syntaxTree.GetLocation(fadeSpan)));
        }
        private void AnalyzeOperationBlock(Analyzer analyzer, OperationBlockAnalysisContext context)
        {
            if (context.OperationBlocks.Length != 1)
            {
                return;
            }

            var owningSymbol = context.OwningSymbol;
            var operation    = context.OperationBlocks[0];

            var(accessesBase, hashedMembers) = analyzer.GetHashedMembers(owningSymbol, operation);
            if (!accessesBase && hashedMembers.IsDefaultOrEmpty)
            {
                return;
            }

            var syntaxTree        = operation.Syntax.SyntaxTree;
            var cancellationToken = context.CancellationToken;

            var option = context.Options.GetOption(CodeStyleOptions2.PreferSystemHashCode, operation.Language, syntaxTree, cancellationToken);

            if (option?.Value != true)
            {
                return;
            }

            var operationLocation   = operation.Syntax.GetLocation();
            var declarationLocation = context.OwningSymbol.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).GetLocation();

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         owningSymbol.Locations[0],
                                         option.Notification.Severity,
                                         new[] { operationLocation, declarationLocation },
                                         ImmutableDictionary <string, string> .Empty));
        }
        private Diagnostic?AnalyzeNamespace(OptionSet optionSet, FileScopedNamespaceDeclarationSyntax declaration)
        {
            var tree   = declaration.SyntaxTree;
            var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations);

            if (!ConvertNamespaceAnalysis.CanOfferUseBlockScoped(optionSet, declaration, forAnalyzer: true))
            {
                return(null);
            }

            // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name.
            // otherwise, if it's not hidden, just squiggle the name.
            var severity           = option.Notification.Severity;
            var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden
                ? declaration.Name.GetLocation()
                : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End));

            return(DiagnosticHelper.Create(
                       this.Descriptor,
                       diagnosticLocation,
                       severity,
                       ImmutableArray.Create(declaration.GetLocation()),
                       ImmutableDictionary <string, string> .Empty));
        }
        private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext)
        {
            var node       = syntaxContext.Node;
            var syntaxTree = node.SyntaxTree;

            // "x is Type y" is only available in C# 7.0 and above. Don't offer this refactoring
            // in projects targeting a lesser version.
            if (((CSharpParseOptions)syntaxTree.Options).LanguageVersion < LanguageVersion.CSharp7)
            {
                return;
            }

            var options           = syntaxContext.Options;
            var cancellationToken = syntaxContext.CancellationToken;

            var styleOption = options.GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck, syntaxTree, cancellationToken);

            if (!styleOption.Value)
            {
                // Bail immediately if the user has disabled this feature.
                return;
            }

            var comparison = (ExpressionSyntax)node;

            var(comparisonLeft, comparisonRight) = comparison switch
            {
                BinaryExpressionSyntax binaryExpression => (binaryExpression.Left, (SyntaxNode)binaryExpression.Right),
                IsPatternExpressionSyntax isPattern => (isPattern.Expression, isPattern.Pattern),
                _ => throw ExceptionUtilities.Unreachable,
            };
            var operand = GetNullCheckOperand(comparisonLeft, comparison.Kind(), comparisonRight)?.WalkDownParentheses();
            if (operand == null)
            {
                return;
            }

            var semanticModel = syntaxContext.SemanticModel;

            if (operand.IsKind(SyntaxKind.CastExpression, out CastExpressionSyntax? castExpression))
            {
                // Unwrap object cast
                var castType = semanticModel.GetTypeInfo(castExpression.Type).Type;
                if (castType?.SpecialType == SpecialType.System_Object)
                {
                    operand = castExpression.Expression;
                }
            }

            if (semanticModel.GetSymbolInfo(comparison, cancellationToken).GetAnySymbol().IsUserDefinedOperator())
            {
                return;
            }

            if (!TryGetTypeCheckParts(semanticModel, operand,
                                      out var declarator,
                                      out var asExpression,
                                      out var localSymbol))
            {
                return;
            }

            var localStatement = declarator.Parent?.Parent;
            var enclosingBlock = localStatement?.Parent;

            if (localStatement == null ||
                enclosingBlock == null)
            {
                return;
            }

            // Don't convert if the as is part of a using statement
            // eg using (var x = y as MyObject) { }
            if (localStatement is UsingStatementSyntax)
            {
                return;
            }

            // Don't convert if the as is part of a local declaration with a using keyword
            // eg using var x = y as MyObject;
            if (localStatement is LocalDeclarationStatementSyntax localDecl && localDecl.UsingKeyword != default)
            {
                return;
            }

            var typeNode = asExpression.Right;
            var asType   = semanticModel.GetTypeInfo(typeNode, cancellationToken).Type;

            if (asType.IsNullable())
            {
                // Not legal to write "x is int? y"
                return;
            }

            if (asType?.TypeKind == TypeKind.Dynamic)
            {
                // Not legal to use dynamic in a pattern.
                return;
            }

            if (!localSymbol.Type.Equals(asType))
            {
                // We have something like:
                //
                //      BaseType b = x as DerivedType;
                //      if (b != null) { ... }
                //
                // It's not necessarily safe to convert this to:
                //
                //      if (x is DerivedType b) { ... }
                //
                // That's because there may be later code that wants to do something like assign a
                // 'BaseType' into 'b'.  As we've now claimed that it must be DerivedType, that
                // won't work.  This might also cause unintended changes like changing overload
                // resolution.  So, we conservatively do not offer the change in a situation like this.
                return;
            }

            // Check if the as operand is ever written up to the point of null check.
            //
            //      var s = field as string;
            //      field = null;
            //      if (s != null) { ... }
            //
            // It's no longer safe to use pattern-matching because 'field is string s' would never be true.
            //
            // Additionally, also bail out if the assigned local is referenced (i.e. read/write/nameof) up to the point of null check.
            //      var s = field as string;
            //      MethodCall(flag: s == null);
            //      if (s != null) { ... }
            //
            var asOperand           = semanticModel.GetSymbolInfo(asExpression.Left, cancellationToken).Symbol;
            var localStatementStart = localStatement.SpanStart;
            var comparisonSpanStart = comparison.SpanStart;

            foreach (var descendentNode in enclosingBlock.DescendantNodes())
            {
                var descendentNodeSpanStart = descendentNode.SpanStart;
                if (descendentNodeSpanStart <= localStatementStart)
                {
                    continue;
                }

                if (descendentNodeSpanStart >= comparisonSpanStart)
                {
                    break;
                }

                if (descendentNode.IsKind(SyntaxKind.IdentifierName, out IdentifierNameSyntax? identifierName))
                {
                    // Check if this is a 'write' to the asOperand.
                    if (identifierName.Identifier.ValueText == asOperand?.Name &&
                        asOperand.Equals(semanticModel.GetSymbolInfo(identifierName, cancellationToken).Symbol) &&
                        identifierName.IsWrittenTo())
                    {
                        return;
                    }

                    // Check is a reference of any sort (i.e. read/write/nameof) to the local.
                    if (identifierName.Identifier.ValueText == localSymbol.Name)
                    {
                        return;
                    }
                }
            }

            if (!Analyzer.CanSafelyConvertToPatternMatching(
                    semanticModel, localSymbol, comparison, operand,
                    localStatement, enclosingBlock, cancellationToken))
            {
                return;
            }

            // Looks good!
            var additionalLocations = ImmutableArray.Create(
                declarator.GetLocation(),
                comparison.GetLocation(),
                asExpression.GetLocation());

            // Put a diagnostic with the appropriate severity on the declaration-statement itself.
            syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                               Descriptor,
                                               localStatement.GetLocation(),
                                               styleOption.Notification.Severity,
                                               additionalLocations,
                                               properties: null));
        }
Beispiel #10
0
        private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext)
        {
            var options           = syntaxContext.Options;
            var syntaxTree        = syntaxContext.Node.SyntaxTree;
            var cancellationToken = syntaxContext.CancellationToken;
            var optionSet         = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var styleOption = optionSet.GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck);

            if (!styleOption.Value)
            {
                // Bail immediately if the user has disabled this feature.
                return;
            }

            var severity = styleOption.Notification.Severity;

            // "x is Type y" is only available in C# 7.0 and above.  Don't offer this refactoring
            // in projects targetting a lesser version.
            if (((CSharpParseOptions)syntaxTree.Options).LanguageVersion < LanguageVersion.CSharp7)
            {
                return;
            }

            var isExpression = (BinaryExpressionSyntax)syntaxContext.Node;

            if (!TryGetPatternPieces(isExpression,
                                     out var ifStatement, out var localDeclarationStatement,
                                     out var declarator, out var castExpression))
            {
                return;
            }

            // It's of the form:
            //
            //     if (expr is Type)
            //     {
            //         var v = (Type)expr;
            //     }

            // Make sure that moving 'v' to the outer scope won't cause any conflicts.

            var ifStatementScope = ifStatement.Parent.IsKind(SyntaxKind.Block)
                ? ifStatement.Parent
                : ifStatement;

            if (ContainsVariableDeclaration(ifStatementScope, declarator))
            {
                // can't switch to using a pattern here as it would cause a scoping
                // problem.
                //
                // TODO(cyrusn): Consider allowing the user to do this, but giving
                // them an error preview.
                return;
            }

            var semanticModel = syntaxContext.SemanticModel;
            var localSymbol   = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator);
            var isType        = semanticModel.GetTypeInfo(castExpression.Type).Type;

            if (isType.IsNullable())
            {
                // not legal to write "if (x is int? y)"
                return;
            }

            if (!localSymbol.Type.Equals(isType))
            {
                // we have something like:
                //
                //      if (x is DerivedType)
                //      {
                //          BaseType b = (DerivedType)x;
                //      }
                //
                // It's not necessarily safe to convert this to:
                //
                //      if (x is DerivedType b) { ... }
                //
                // That's because there may be later code that wants to do something like assign a
                // 'BaseType' into 'b'.  As we've now claimed that it must be DerivedType, that
                // won't work.  This might also cause unintended changes like changing overload
                // resolution.  So, we conservatively do not offer the change in a situation like this.
                return;
            }

            // Looks good!
            var additionalLocations = ImmutableArray.Create(
                ifStatement.GetLocation(),
                localDeclarationStatement.GetLocation());

            // Put a diagnostic with the appropriate severity on the declaration-statement itself.
            syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                               Descriptor,
                                               localDeclarationStatement.GetLocation(),
                                               severity,
                                               additionalLocations,
                                               properties: null));
        }
        private void AnalyzeOperation(OperationAnalysisContext context, IOperation operation, IOperation instanceOperation)
        {
            // this is a static reference so we don't care if it's qualified
            if (instanceOperation == null)
            {
                return;
            }

            // if we're not referencing `this.` or `Me.` (e.g., a parameter, local, etc.)
            if (instanceOperation.Kind != OperationKind.InstanceReference)
            {
                return;
            }

            // Initializer lists are IInvocationOperation which if passed to GetApplicableOptionFromSymbolKind
            // will incorrectly fetch the options for method call.
            // We still want to handle InstanceReferenceKind.ContainingTypeInstance
            if ((instanceOperation as IInstanceReferenceOperation)?.ReferenceKind == InstanceReferenceKind.ImplicitReceiver)
            {
                return;
            }

            // If we can't be qualified (e.g., because we're already qualified with `base.`), we're done.
            if (!CanMemberAccessBeQualified(context.ContainingSymbol, instanceOperation.Syntax))
            {
                return;
            }

            // if we can't find a member then we can't do anything.  Also, we shouldn't qualify
            // accesses to static members.
            if (IsStaticMemberOrTargetMethod(operation))
            {
                return;
            }

            var simpleName = instanceOperation.Syntax as TSimpleNameSyntax;

            if (simpleName == null)
            {
                return;
            }

            var syntaxTree        = context.Operation.Syntax.SyntaxTree;
            var cancellationToken = context.CancellationToken;
            var optionSet         = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var applicableOption = QualifyMembersHelpers.GetApplicableOptionFromSymbolKind(operation);
            var optionValue      = optionSet.GetOption(applicableOption, context.Operation.Syntax.Language);

            var shouldOptionBePresent = optionValue.Value;
            var severity = optionValue.Notification.Severity;

            if (!shouldOptionBePresent || severity == ReportDiagnostic.Suppress)
            {
                return;
            }

            if (!IsAlreadyQualifiedMemberAccess(simpleName))
            {
                context.ReportDiagnostic(DiagnosticHelper.Create(
                                             Descriptor,
                                             GetLocation(operation),
                                             severity,
                                             additionalLocations: null,
                                             properties: null));
            }
        }
Beispiel #12
0
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
        {
            var conditionalExpression = (TConditionalExpressionSyntax)context.Node;

            var syntaxTree        = context.Node.SyntaxTree;
            var cancellationToken = context.CancellationToken;
            var optionSet         = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var option = optionSet.GetOption(CodeStyleOptions.PreferCoalesceExpression, conditionalExpression.Language);

            if (!option.Value)
            {
                return;
            }

            var syntaxFacts = this.GetSyntaxFactsService();

            syntaxFacts.GetPartsOfConditionalExpression(
                conditionalExpression, out var conditionNode, out var whenTrueNodeHigh, out var whenFalseNodeHigh);

            conditionNode = syntaxFacts.WalkDownParentheses(conditionNode);
            var whenTrueNodeLow  = syntaxFacts.WalkDownParentheses(whenTrueNodeHigh);
            var whenFalseNodeLow = syntaxFacts.WalkDownParentheses(whenFalseNodeHigh);

            var condition = conditionNode as TBinaryExpressionSyntax;

            if (condition == null)
            {
                return;
            }

            var isEquals    = IsEquals(condition);
            var isNotEquals = IsNotEquals(condition);

            if (!isEquals && !isNotEquals)
            {
                return;
            }

            syntaxFacts.GetPartsOfBinaryExpression(condition, out var conditionLeftHigh, out var conditionRightHigh);

            var conditionLeftLow  = syntaxFacts.WalkDownParentheses(conditionLeftHigh);
            var conditionRightLow = syntaxFacts.WalkDownParentheses(conditionRightHigh);

            var conditionLeftIsNull  = syntaxFacts.IsNullLiteralExpression(conditionLeftLow);
            var conditionRightIsNull = syntaxFacts.IsNullLiteralExpression(conditionRightLow);

            if (conditionRightIsNull && conditionLeftIsNull)
            {
                // null == null    nothing to do here.
                return;
            }

            if (!conditionRightIsNull && !conditionLeftIsNull)
            {
                return;
            }

            if (!syntaxFacts.AreEquivalent(
                    conditionRightIsNull ? conditionLeftLow : conditionRightLow,
                    isEquals ? whenFalseNodeLow : whenTrueNodeLow))
            {
                return;
            }

            var semanticModel = context.SemanticModel;
            var conditionType = semanticModel.GetTypeInfo(
                conditionLeftIsNull ? conditionRightLow : conditionLeftLow, cancellationToken).Type;

            if (conditionType != null &&
                !conditionType.IsReferenceType)
            {
                // Note: it is intentional that we do not support nullable types here.  If you have:
                //
                //  int? x;
                //  var z = x == null ? y : x;
                //
                // then that's not the same as:   x ?? y.   ?? will unwrap the nullable, producing a
                // int and not an int? like we have in the above code.
                //
                // Note: we could look for:  x == null ? y : x.Value, and simplify that in the future.
                return;
            }

            var conditionPartToCheck = conditionRightIsNull ? conditionLeftHigh : conditionRightHigh;
            var whenPartToCheck      = isEquals ? whenTrueNodeHigh : whenFalseNodeHigh;
            var locations            = ImmutableArray.Create(
                conditionalExpression.GetLocation(),
                conditionPartToCheck.GetLocation(),
                whenPartToCheck.GetLocation());

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         conditionalExpression.GetLocation(),
                                         option.Notification.Severity,
                                         locations,
                                         properties: null));
        }
Beispiel #13
0
        protected override void InitializeWorker(AnalysisContext context)
        {
            context.RegisterCompilationStartAction(compilationStartContext =>
            {
                // State map for fields:
                //  'isCandidate' : Indicates whether the field is a candidate to be made readonly based on it's options.
                //  'written'     : Indicates if there are any writes to the field outside the constructor and field initializer.
                var fieldStateMap = new ConcurrentDictionary <IFieldSymbol, (bool isCandidate, bool written)>();

                var threadStaticAttribute = compilationStartContext.Compilation.ThreadStaticAttributeType();
                var dataContractAttribute = compilationStartContext.Compilation.DataContractAttribute();
                var dataMemberAttribute   = compilationStartContext.Compilation.DataMemberAttribute();

                // We register following actions in the compilation:
                // 1. A symbol action for field symbols to ensure the field state is initialized for every field in
                //    the compilation.
                // 2. An operation action for field references to detect if a candidate field is written outside
                //    constructor and field initializer, and update field state accordingly.
                // 3. A symbol start/end action for named types to report diagnostics for candidate fields that were
                //    not written outside constructor and field initializer.

                compilationStartContext.RegisterSymbolAction(AnalyzeFieldSymbol, SymbolKind.Field);

                compilationStartContext.RegisterSymbolStartAction(symbolStartContext =>
                {
                    symbolStartContext.RegisterOperationAction(AnalyzeOperation, OperationKind.FieldReference);
                    symbolStartContext.RegisterSymbolEndAction(OnSymbolEnd);
                }, SymbolKind.NamedType);

                return;

                // Local functions.
                void AnalyzeFieldSymbol(SymbolAnalysisContext symbolContext)
                {
                    _ = TryGetOrInitializeFieldState((IFieldSymbol)symbolContext.Symbol, symbolContext.Options, symbolContext.CancellationToken);
                }

                void AnalyzeOperation(OperationAnalysisContext operationContext)
                {
                    var fieldReference        = (IFieldReferenceOperation)operationContext.Operation;
                    var(isCandidate, written) = TryGetOrInitializeFieldState(fieldReference.Field, operationContext.Options, operationContext.CancellationToken);

                    // Ignore fields that are not candidates or have already been written outside the constructor/field initializer.
                    if (!isCandidate || written)
                    {
                        return;
                    }

                    // Check if this is a field write outside constructor and field initializer, and update field state accordingly.
                    if (IsFieldWrite(fieldReference, operationContext.ContainingSymbol))
                    {
                        UpdateFieldStateOnWrite(fieldReference.Field);
                    }
                }

                void OnSymbolEnd(SymbolAnalysisContext symbolEndContext)
                {
                    // Report diagnostics for candidate fields that are not written outside constructor and field initializer.
                    var members = ((INamedTypeSymbol)symbolEndContext.Symbol).GetMembers();
                    foreach (var member in members)
                    {
                        if (member is IFieldSymbol field && fieldStateMap.TryRemove(field, out var value))
                        {
                            var(isCandidate, written) = value;
                            if (isCandidate && !written)
                            {
                                var option     = GetCodeStyleOption(field, symbolEndContext.Options);
                                var diagnostic = DiagnosticHelper.Create(
                                    Descriptor,
                                    field.Locations[0],
                                    option.Notification.Severity,
                                    additionalLocations: null,
                                    properties: null);
                                symbolEndContext.ReportDiagnostic(diagnostic);
                            }
                        }
                    }
                }

                static bool IsCandidateField(IFieldSymbol symbol, INamedTypeSymbol threadStaticAttribute, INamedTypeSymbol dataContractAttribute, INamedTypeSymbol dataMemberAttribute) =>
                symbol.DeclaredAccessibility == Accessibility.Private &&
                !symbol.IsReadOnly &&
                !symbol.IsConst &&
                !symbol.IsImplicitlyDeclared &&
                symbol.Locations.Length == 1 &&
                symbol.Type.IsMutableValueType() == false &&
                !symbol.IsFixedSizeBuffer &&
                !symbol.GetAttributes().Any(
Beispiel #14
0
        private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ienumerableType)
        {
            if (!AreCollectionInitializersSupported(context))
            {
                return;
            }

            var semanticModel            = context.SemanticModel;
            var objectCreationExpression = (TObjectCreationExpressionSyntax)context.Node;
            var language          = objectCreationExpression.Language;
            var cancellationToken = context.CancellationToken;

            var option = context.GetOption(CodeStyleOptions.PreferCollectionInitializer, language);

            if (!option.Value)
            {
                // not point in analyzing if the option is off.
                return;
            }

            // Object creation can only be converted to collection initializer if it
            // implements the IEnumerable type.
            var objectType = context.SemanticModel.GetTypeInfo(objectCreationExpression, cancellationToken);

            if (objectType.Type == null || !objectType.Type.AllInterfaces.Contains(ienumerableType))
            {
                return;
            }

            var matches = ObjectCreationExpressionAnalyzer <TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TInvocationExpressionSyntax, TExpressionStatementSyntax, TVariableDeclaratorSyntax> .Analyze(
                semanticModel, GetSyntaxFactsService(), objectCreationExpression, cancellationToken);

            if (matches == null || matches.Value.Length == 0)
            {
                return;
            }

            var containingStatement = objectCreationExpression.FirstAncestorOrSelf <TStatementSyntax>();

            if (containingStatement == null)
            {
                return;
            }

            var nodes       = ImmutableArray.Create <SyntaxNode>(containingStatement).AddRange(matches.Value);
            var syntaxFacts = GetSyntaxFactsService();

            if (syntaxFacts.ContainsInterleavedDirective(nodes, cancellationToken))
            {
                return;
            }

            var locations = ImmutableArray.Create(objectCreationExpression.GetLocation());

            var severity = option.Notification.Severity;

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         objectCreationExpression.GetLocation(),
                                         severity,
                                         additionalLocations: locations,
                                         properties: null));

            FadeOutCode(context, matches.Value, locations);
        }
Beispiel #15
0
        protected bool TrySimplifyTypeNameExpression(SemanticModel model, SyntaxNode node, AnalyzerOptions analyzerOptions, out Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            diagnostic = default;

            var syntaxTree = node.SyntaxTree;
            var optionSet  = analyzerOptions.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return(false);
            }

            if (!CanSimplifyTypeNameExpressionCore(
                    model, node, optionSet,
                    out var issueSpan, out var diagnosticId, out var inDeclaration,
                    cancellationToken))
            {
                return(false);
            }

            if (model.SyntaxTree.OverlapsHiddenPosition(issueSpan, cancellationToken))
            {
                return(false);
            }

            PerLanguageOption <CodeStyleOption <bool> > option;
            DiagnosticDescriptor descriptor;
            ReportDiagnostic     severity;

            switch (diagnosticId)
            {
            case IDEDiagnosticIds.SimplifyNamesDiagnosticId:
                descriptor = s_descriptorSimplifyNames;
                severity   = descriptor.DefaultSeverity.ToReportDiagnostic();
                break;

            case IDEDiagnosticIds.SimplifyMemberAccessDiagnosticId:
                descriptor = s_descriptorSimplifyMemberAccess;
                severity   = descriptor.DefaultSeverity.ToReportDiagnostic();
                break;

            case IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId:
                option = inDeclaration
                        ? CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration
                        : CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess;
                descriptor = s_descriptorPreferBuiltinOrFrameworkType;

                var optionValue = optionSet.GetOption(option, GetLanguageName());
                severity = optionValue.Notification.Severity;
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(diagnosticId);
            }

            if (descriptor == null)
            {
                return(false);
            }

            var tree    = model.SyntaxTree;
            var builder = ImmutableDictionary.CreateBuilder <string, string>();

            builder["OptionName"]     = nameof(CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess); // TODO: need the actual one
            builder["OptionLanguage"] = model.Language;
            diagnostic = DiagnosticHelper.Create(descriptor, tree.GetLocation(issueSpan), severity, additionalLocations: null, builder.ToImmutable());
            return(true);
        }
        private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol expressionTypeOpt)
        {
            var argumentNode = (ArgumentSyntax)context.Node;
            var csOptions    = (CSharpParseOptions)context.Node.SyntaxTree.Options;

            if (csOptions.LanguageVersion < LanguageVersion.CSharp7)
            {
                // out-vars are not supported prior to C# 7.0.
                return;
            }

            var options           = context.Options;
            var syntaxTree        = context.Node.SyntaxTree;
            var cancellationToken = context.CancellationToken;

            var option = options.GetOption(CSharpCodeStyleOptions.PreferInlinedVariableDeclaration, syntaxTree, cancellationToken);

            if (!option.Value)
            {
                // Don't bother doing any work if the user doesn't even have this preference set.
                return;
            }

            if (argumentNode.RefOrOutKeyword.Kind() != SyntaxKind.OutKeyword)
            {
                // Immediately bail if this is not an out-argument.  If it's not an out-argument
                // we clearly can't convert it to be an out-variable-declaration.
                return;
            }

            var argumentExpression = argumentNode.Expression;

            if (!argumentExpression.IsKind(SyntaxKind.IdentifierName, out IdentifierNameSyntax identifierName))
            {
                // has to be exactly the form "out i".  i.e. "out this.i" or "out v[i]" are legal
                // cases for out-arguments, but could not be converted to an out-variable-declaration.
                return;
            }

            if (!(argumentNode.Parent is ArgumentListSyntax argumentList))
            {
                return;
            }

            var invocationOrCreation = argumentList.Parent;

            if (!invocationOrCreation.IsKind(SyntaxKind.InvocationExpression) &&
                !invocationOrCreation.IsKind(SyntaxKind.ObjectCreationExpression))
            {
                // Out-variables are only legal with invocations and object creations.
                // If we don't have one of those bail.  Note: we need hte parent to be
                // one of these forms so we can accurately verify that inlining the
                // variable doesn't change semantics.
                return;
            }

            // Don't offer to inline variables named "_".  It can cause is to create a discard symbol
            // which would cause a break.
            if (identifierName.Identifier.ValueText == "_")
            {
                return;
            }

            var containingStatement = argumentExpression.FirstAncestorOrSelf <StatementSyntax>();

            if (containingStatement == null)
            {
                return;
            }

            var semanticModel = context.SemanticModel;

            if (!(semanticModel.GetSymbolInfo(argumentExpression, cancellationToken).Symbol is ILocalSymbol outLocalSymbol))
            {
                // The out-argument wasn't referencing a local.  So we don't have an local
                // declaration that we can attempt to inline here.
                return;
            }

            // Ensure that the local-symbol actually belongs to LocalDeclarationStatement.
            // Trying to do things like inline a var-decl in a for-statement is just too
            // esoteric and would make us have to write a lot more complex code to support
            // that scenario.
            var localReference = outLocalSymbol.DeclaringSyntaxReferences.FirstOrDefault();

            if (!(localReference?.GetSyntax(cancellationToken) is VariableDeclaratorSyntax localDeclarator))
            {
                return;
            }

            var localDeclaration = localDeclarator.Parent as VariableDeclarationSyntax;

            if (!(localDeclaration?.Parent is LocalDeclarationStatementSyntax localStatement))
            {
                return;
            }

            if (localDeclarator.SpanStart >= argumentNode.SpanStart)
            {
                // We have an error situation where the local was declared after the out-var.
                // Don't even bother offering anything here.
                return;
            }

            // If the local has an initializer, only allow the refactoring if it is initialized
            // with a simple literal or 'default' expression.  i.e. it's ok to inline "var v = 0"
            // since there are no side-effects of the initialization.  However something like
            // "var v = M()" should not be inlined as that could break program semantics.
            if (localDeclarator.Initializer != null)
            {
                if (!(localDeclarator.Initializer.Value is LiteralExpressionSyntax) &&
                    !(localDeclarator.Initializer.Value is DefaultExpressionSyntax))
                {
                    return;
                }
            }

            // Get the block that the local is scoped inside of.  We'll search that block
            // for references to the local to make sure that no reads/writes happen before
            // the out-argument.  If there are any reads/writes we can't inline as those
            // accesses will become invalid.
            if (!(localStatement.Parent is BlockSyntax enclosingBlockOfLocalStatement))
            {
                return;
            }

            if (argumentExpression.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken))
            {
                // out-vars are not allowed inside expression-trees.  So don't offer to
                // fix if we're inside one.
                return;
            }

            // Find the scope that the out-declaration variable will live in after we
            // rewrite things.
            var outArgumentScope = GetOutArgumentScope(argumentExpression);

            if (!outLocalSymbol.CanSafelyMoveLocalToBlock(enclosingBlockOfLocalStatement, outArgumentScope))
            {
                return;
            }

            // Make sure that variable is not accessed outside of that scope.
            var dataFlow = semanticModel.AnalyzeDataFlow(outArgumentScope);

            if (dataFlow.ReadOutside.Contains(outLocalSymbol) || dataFlow.WrittenOutside.Contains(outLocalSymbol))
            {
                // The variable is read or written from outside the block that the new variable
                // would be scoped in.  This would cause a break.
                //
                // Note(cyrusn): We could still offer the refactoring, but just show an error in the
                // preview in this case.
                return;
            }

            // Make sure the variable isn't ever accessed before the usage in this out-var.
            if (IsAccessed(semanticModel, outLocalSymbol, enclosingBlockOfLocalStatement,
                           localStatement, argumentNode, cancellationToken))
            {
                return;
            }

            // See if inlining this variable would make it so that some variables were no
            // longer definitely assigned.
            if (WouldCauseDefiniteAssignmentErrors(semanticModel, localStatement,
                                                   enclosingBlockOfLocalStatement, outLocalSymbol))
            {
                return;
            }

            // Collect some useful nodes for the fix provider to use so it doesn't have to
            // find them again.
            var allLocations = ImmutableArray.Create(
                localDeclarator.GetLocation(),
                identifierName.GetLocation(),
                invocationOrCreation.GetLocation(),
                containingStatement.GetLocation());

            // If the local variable only has one declarator, then report the suggestion on the whole
            // declaration.  Otherwise, return the suggestion only on the single declarator.
            var reportNode = localDeclaration.Variables.Count == 1
                ? (SyntaxNode)localDeclaration
                : localDeclarator;

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         reportNode.GetLocation(),
                                         option.Notification.Severity,
                                         additionalLocations: allLocations,
                                         properties: null));
        }
        private void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            var cancellationToken = context.CancellationToken;
            var node = (TMemberAccessExpressionSyntax)context.Node;

            var syntaxFacts = GetSyntaxFacts();
            var expr        = syntaxFacts.GetExpressionOfMemberAccessExpression(node);

            if (!(expr is TThisExpressionSyntax))
            {
                return;
            }

            var analyzerOptions = context.Options;

            var syntaxTree = node.SyntaxTree;
            var optionSet  = analyzerOptions.GetAnalyzerOptionSet(syntaxTree, cancellationToken);

            var model = context.SemanticModel;

            if (!CanSimplifyTypeNameExpression(
                    model, node, optionSet, out var issueSpan, cancellationToken))
            {
                return;
            }

            if (model.SyntaxTree.OverlapsHiddenPosition(issueSpan, cancellationToken))
            {
                return;
            }

            var symbolInfo = model.GetSymbolInfo(node, cancellationToken);

            if (symbolInfo.Symbol == null)
            {
                return;
            }

            var applicableOption = QualifyMembersHelpers.GetApplicableOptionFromSymbolKind(symbolInfo.Symbol.Kind);
            var optionValue      = optionSet.GetOption(applicableOption, GetLanguageName());

            if (optionValue == null)
            {
                return;
            }

            var severity = optionValue.Notification.Severity;

            var tree    = model.SyntaxTree;
            var builder = ImmutableDictionary.CreateBuilder <string, string>();

            // used so we can provide a link in the preview to the options page. This value is
            // hard-coded there to be the one that will go to the code-style page.
            builder["OptionName"]     = nameof(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration);
            builder["OptionLanguage"] = model.Language;

            var diagnostic = DiagnosticHelper.Create(
                Descriptor, tree.GetLocation(issueSpan), severity,
                ImmutableArray.Create(node.GetLocation()), builder.ToImmutable());

            context.ReportDiagnostic(diagnostic);
        }
        private void AnalyzeSyntax(
            SyntaxNodeAnalysisContext context,
            INamedTypeSymbol?expressionTypeOpt,
            IMethodSymbol?referenceEqualsMethodOpt)
        {
            var conditionalExpression = (TConditionalExpressionSyntax)context.Node;

            if (!ShouldAnalyze(conditionalExpression.SyntaxTree.Options))
            {
                return;
            }

            var option = context.GetOption(CodeStyleOptions2.PreferNullPropagation, conditionalExpression.Language);

            if (!option.Value)
            {
                return;
            }

            var syntaxFacts = GetSyntaxFacts();

            syntaxFacts.GetPartsOfConditionalExpression(
                conditionalExpression, out var conditionNode, out var whenTrueNode, out var whenFalseNode);

            conditionNode = syntaxFacts.WalkDownParentheses(conditionNode);

            var conditionIsNegated = false;

            if (syntaxFacts.IsLogicalNotExpression(conditionNode))
            {
                conditionIsNegated = true;
                conditionNode      = syntaxFacts.WalkDownParentheses(
                    syntaxFacts.GetOperandOfPrefixUnaryExpression(conditionNode));
            }

            if (!TryAnalyzeCondition(
                    context, syntaxFacts, referenceEqualsMethodOpt, conditionNode,
                    out var conditionPartToCheck, out var isEquals))
            {
                return;
            }

            if (conditionIsNegated)
            {
                isEquals = !isEquals;
            }

            // Needs to be of the form:
            //      x == null ? null : ...    or
            //      x != null ? ...  : null;
            if (isEquals && !syntaxFacts.IsNullLiteralExpression(whenTrueNode))
            {
                return;
            }

            if (!isEquals && !syntaxFacts.IsNullLiteralExpression(whenFalseNode))
            {
                return;
            }

            var whenPartToCheck = isEquals ? whenFalseNode : whenTrueNode;

            var semanticModel = context.SemanticModel;
            var whenPartMatch = GetWhenPartMatch(syntaxFacts, semanticModel, conditionPartToCheck, whenPartToCheck);

            if (whenPartMatch == null)
            {
                return;
            }

            // ?. is not available in expression-trees.  Disallow the fix in that case.

            var type = semanticModel.GetTypeInfo(conditionalExpression).Type;

            if (type?.IsValueType == true)
            {
                if (!(type is INamedTypeSymbol namedType) || namedType.ConstructedFrom.SpecialType != SpecialType.System_Nullable_T)
                {
                    // User has something like:  If(str is nothing, nothing, str.Length)
                    // In this case, converting to str?.Length changes the type of this from
                    // int to int?
                    return;
                }
                // But for a nullable type, such as  If(c is nothing, nothing, c.nullable)
                // converting to c?.nullable doesn't affect the type
            }

            if (IsInExpressionTree(semanticModel, conditionNode, expressionTypeOpt, context.CancellationToken))
            {
                return;
            }

            var locations = ImmutableArray.Create(
                conditionalExpression.GetLocation(),
                conditionPartToCheck.GetLocation(),
                whenPartToCheck.GetLocation());

            var properties         = ImmutableDictionary <string, string> .Empty;
            var whenPartIsNullable = semanticModel.GetTypeInfo(whenPartMatch).Type?.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;

            if (whenPartIsNullable)
            {
                properties = properties.Add(UseNullPropagationConstants.WhenPartIsNullable, "");
            }

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         conditionalExpression.GetLocation(),
                                         option.Notification.Severity,
                                         locations,
                                         properties));
        }
        internal static Diagnostic CreateDiagnostic(SemanticModel model, OptionSet optionSet, TextSpan issueSpan, string diagnosticId, bool inDeclaration)
        {
            PerLanguageOption2 <CodeStyleOption2 <bool> > option;
            DiagnosticDescriptor descriptor;
            ReportDiagnostic     severity;

            switch (diagnosticId)
            {
            case IDEDiagnosticIds.SimplifyNamesDiagnosticId:
                descriptor = s_descriptorSimplifyNames;
                severity   = descriptor.DefaultSeverity.ToReportDiagnostic();
                break;

            case IDEDiagnosticIds.SimplifyMemberAccessDiagnosticId:
                descriptor = s_descriptorSimplifyMemberAccess;
                severity   = descriptor.DefaultSeverity.ToReportDiagnostic();
                break;

            case IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId:
                option = inDeclaration
                        ? CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration
                        : CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInMemberAccess;
                descriptor = s_descriptorPreferBuiltinOrFrameworkType;

                var optionValue = optionSet.GetOption(option, model.Language) !;
                severity = optionValue.Notification.Severity;
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(diagnosticId);
            }

            var tree    = model.SyntaxTree;
            var builder = ImmutableDictionary.CreateBuilder <string, string>();

            builder["OptionName"]     = nameof(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInMemberAccess); // TODO: need the actual one
            builder["OptionLanguage"] = model.Language;
            var diagnostic = DiagnosticHelper.Create(descriptor, tree.GetLocation(issueSpan), severity, additionalLocations: null, builder.ToImmutable());

#if LOG
            var sourceText = tree.GetText();
            sourceText.GetLineAndOffset(issueSpan.Start, out var startLineNumber, out var startOffset);
            sourceText.GetLineAndOffset(issueSpan.End, out var endLineNumber, out var endOffset);
            var logLine = tree.FilePath + "," + startLineNumber + "\t" + diagnosticId + "\t" + inDeclaration + "\t";

            var leading = sourceText.ToString(TextSpan.FromBounds(
                                                  sourceText.Lines[startLineNumber].Start, issueSpan.Start));
            var mid      = sourceText.ToString(issueSpan);
            var trailing = sourceText.ToString(TextSpan.FromBounds(
                                                   issueSpan.End, sourceText.Lines[endLineNumber].End));

            var contents = leading + "[|" + s_newlinePattern.Replace(mid, " ") + "|]" + trailing;
            logLine += contents + "\r\n";

            lock (_logGate)
            {
                File.AppendAllText(_logFile, logLine);
            }
#endif

            return(diagnostic);
        }
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
        {
            var switchStatement = context.Node;

            if (switchStatement.ContainsDirectives)
            {
                return;
            }

            var syntaxTree = switchStatement.SyntaxTree;

            if (((CSharpParseOptions)syntaxTree.Options).LanguageVersion < LanguageVersion.CSharp8)
            {
                return;
            }

            var options           = context.Options;
            var cancellationToken = context.CancellationToken;
            var optionSet         = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var styleOption = optionSet.GetOption(CSharpCodeStyleOptions.PreferSwitchExpression);

            if (!styleOption.Value)
            {
                // User has disabled this feature.
                return;
            }

            if (switchStatement.GetDiagnostics().Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
            {
                return;
            }

            var(nodeToGenerate, declaratorToRemoveOpt) =
                Analyzer.Analyze(
                    (SwitchStatementSyntax)switchStatement,
                    context.SemanticModel,
                    out var shouldRemoveNextStatement);
            if (nodeToGenerate == default)
            {
                return;
            }

            var additionalLocations = ArrayBuilder <Location> .GetInstance();

            additionalLocations.Add(switchStatement.GetLocation());
            additionalLocations.AddOptional(declaratorToRemoveOpt?.GetLocation());

            context.ReportDiagnostic(DiagnosticHelper.Create(Descriptor,
                                                             // Report the diagnostic on the "switch" keyword.
                                                             location: switchStatement.GetFirstToken().GetLocation(),
                                                             effectiveSeverity: styleOption.Notification.Severity,
                                                             additionalLocations: additionalLocations.ToArrayAndFree(),
                                                             properties: ImmutableDictionary <string, string> .Empty
                                                             .Add(Constants.NodeToGenerateKey, ((int)nodeToGenerate).ToString(CultureInfo.InvariantCulture))
                                                             .Add(Constants.ShouldRemoveNextStatementKey, shouldRemoveNextStatement.ToString(CultureInfo.InvariantCulture))));
        }
Beispiel #21
0
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
        {
            var outermostUsing = (UsingStatementSyntax)context.Node;

            var syntaxTree = context.Node.SyntaxTree;
            var options    = (CSharpParseOptions)syntaxTree.Options;

            if (options.LanguageVersion < LanguageVersion.CSharp8)
            {
                return;
            }

            if (!(outermostUsing.Parent is BlockSyntax parentBlock))
            {
                // Don't offer on a using statement that is parented by another using statement.
                // We'll just offer on the topmost using statement.
                return;
            }

            var innermostUsing = outermostUsing;

            // Check that all the immediately nested usings are convertible as well.
            // We don't want take a sequence of nested-using and only convert some of them.
            for (var current = outermostUsing; current != null; current = current.Statement as UsingStatementSyntax)
            {
                innermostUsing = current;
                if (current.Declaration == null)
                {
                    return;
                }
            }

            // Verify that changing this using-statement into a using-declaration will not
            // change semantics.
            if (!PreservesSemantics(parentBlock, outermostUsing, innermostUsing))
            {
                return;
            }

            var cancellationToken = context.CancellationToken;

            // Converting a using-statement to a using-variable-declaration will cause the using's
            // variables to now be pushed up to the parent block's scope. This is also true for any
            // local variables in the innermost using's block. These may then collide with other
            // variables in the block, causing an error.  Check for that and bail if this happens.
            if (CausesVariableCollision(
                    context.SemanticModel, parentBlock,
                    outermostUsing, innermostUsing, cancellationToken))
            {
                return;
            }

            var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferSimpleUsingStatement);

            if (!option.Value)
            {
                return;
            }

            // Good to go!
            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         outermostUsing.UsingKeyword.GetLocation(),
                                         option.Notification.Severity,
                                         additionalLocations: ImmutableArray.Create(outermostUsing.GetLocation()),
                                         properties: null));
        }
 private Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, SyntaxNode declaration, TextSpan diagnosticSpan, ReportDiagnostic severity)
 => DiagnosticHelper.Create(descriptor, declaration.SyntaxTree.GetLocation(diagnosticSpan), severity, additionalLocations: null, properties: null);
Beispiel #23
0
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, IMethodSymbol referenceEqualsMethod)
        {
            var cancellationToken = context.CancellationToken;

            var semanticModel = context.SemanticModel;
            var syntaxTree    = semanticModel.SyntaxTree;

            if (!IsLanguageVersionSupported(syntaxTree.Options))
            {
                return;
            }

            var option = context.GetOption(CodeStyleOptions.PreferIsNullCheckOverReferenceEqualityMethod, semanticModel.Language);

            if (!option.Value)
            {
                return;
            }

            var invocation  = context.Node;
            var syntaxFacts = GetSyntaxFacts();

            var expression = syntaxFacts.GetExpressionOfInvocationExpression(invocation);
            var nameNode   = syntaxFacts.IsIdentifierName(expression)
                ? expression
                : syntaxFacts.IsSimpleMemberAccessExpression(expression)
                    ? syntaxFacts.GetNameOfMemberAccessExpression(expression)
                    : null;

            if (!syntaxFacts.IsIdentifierName(nameNode))
            {
                return;
            }

            syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out _);
            if (!syntaxFacts.StringComparer.Equals(name, nameof(ReferenceEquals)))
            {
                return;
            }

            var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(invocation);

            if (arguments.Count != 2)
            {
                return;
            }

            if (!MatchesPattern(syntaxFacts, arguments[0], arguments[1]) &&
                !MatchesPattern(syntaxFacts, arguments[1], arguments[0]))
            {
                return;
            }

            var symbol = semanticModel.GetSymbolInfo(invocation, cancellationToken).Symbol;

            if (!referenceEqualsMethod.Equals(symbol))
            {
                return;
            }

            var properties = ImmutableDictionary <string, string> .Empty.Add(
                UseIsNullConstants.Kind, UseIsNullConstants.ReferenceEqualsKey);

            var genericParameterSymbol = GetGenericParameterSymbol(syntaxFacts, semanticModel, arguments[0], arguments[1], cancellationToken);

            if (genericParameterSymbol != null)
            {
                if (genericParameterSymbol.HasValueTypeConstraint)
                {
                    // 'is null' would generate error CS0403: Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
                    // '== null' would generate error CS0019: Operator '==' cannot be applied to operands of type 'T' and '<null>'
                    // 'Is Nothing' would generate error BC30020: 'Is' operator does not accept operands of type 'T'. Operands must be reference or nullable types.
                    return;
                }

                if (!genericParameterSymbol.HasReferenceTypeConstraint)
                {
                    // Needs special casing for C# as long as
                    // https://github.com/dotnet/csharplang/issues/1284
                    // is not implemented.
                    properties = properties.Add(AbstractUseIsNullCheckForReferenceEqualsCodeFixProvider.UnconstrainedGeneric, "");
                }
            }

            var additionalLocations = ImmutableArray.Create(invocation.GetLocation());

            var negated = syntaxFacts.IsLogicalNotExpression(invocation.Parent);

            if (negated)
            {
                properties = properties.Add(AbstractUseIsNullCheckForReferenceEqualsCodeFixProvider.Negated, "");
            }

            var severity = option.Notification.Severity;

            context.ReportDiagnostic(
                DiagnosticHelper.Create(
                    Descriptor, nameNode.GetLocation(),
                    severity,
                    additionalLocations, properties));
        }
        private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol expressionTypeOpt)
        {
            var syntaxTree = context.Operation.Syntax.SyntaxTree;

            if (!IsSupported(syntaxTree.Options))
            {
                return;
            }

            var cancellationToken = context.CancellationToken;

            var throwOperation       = (IThrowOperation)context.Operation;
            var throwStatementSyntax = throwOperation.Syntax;

            var semanticModel = context.Operation.SemanticModel;

            var ifOperation = GetContainingIfOperation(
                semanticModel, throwOperation, cancellationToken);

            // This throw statement isn't parented by an if-statement.  Nothing to
            // do here.
            if (ifOperation == null)
            {
                return;
            }

            if (ifOperation.WhenFalse != null)
            {
                // Can't offer this if the 'if-statement' has an 'else-clause'.
                return;
            }

            var option = context.GetOption(_preferThrowExpressionOption);

            if (!option.Value)
            {
                return;
            }

            if (IsInExpressionTree(semanticModel, throwStatementSyntax, expressionTypeOpt, cancellationToken))
            {
                return;
            }

            if (!(ifOperation.Parent is IBlockOperation containingBlock))
            {
                return;
            }

            if (!TryDecomposeIfCondition(ifOperation, out var localOrParameter))
            {
                return;
            }

            if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter,
                                             out var expressionStatement, out var assignmentExpression))
            {
                return;
            }

            if (!localOrParameter.GetSymbolType().CanAddNullCheck())
            {
                return;
            }

            // We found an assignment using this local/parameter.  Now, just make sure there
            // were no intervening accesses between the check and the assignment.
            if (ValueIsAccessed(
                    semanticModel, ifOperation, containingBlock,
                    localOrParameter, expressionStatement, assignmentExpression))
            {
                return;
            }

            // Ok, there were no intervening writes or accesses.  This check+assignment can be simplified.
            var allLocations = ImmutableArray.Create(
                ifOperation.Syntax.GetLocation(),
                throwOperation.Exception.Syntax.GetLocation(),
                assignmentExpression.Value.Syntax.GetLocation());

            context.ReportDiagnostic(
                DiagnosticHelper.Create(Descriptor, throwStatementSyntax.GetLocation(), option.Notification.Severity, additionalLocations: allLocations, properties: null));
        }
        public void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            var statement         = context.Node;
            var cancellationToken = context.CancellationToken;

            var option = context.Options.GetOption(CSharpCodeStyleOptions.PreferBraces, statement.SyntaxTree, cancellationToken);

            if (option.Value == PreferBracesPreference.None)
            {
                return;
            }

            var embeddedStatement = statement.GetEmbeddedStatement();

            switch (embeddedStatement.Kind())
            {
            case SyntaxKind.Block:
                // The embedded statement already has braces, which is always allowed.
                return;

            case SyntaxKind.IfStatement when statement.Kind() == SyntaxKind.ElseClause:
                // Constructs like the following are always allowed:
                //
                //   if (something)
                //   {
                //   }
                //   else if (somethingElse) // <-- 'if' nested in an 'else' clause
                //   {
                //   }
                return;

            case SyntaxKind.LockStatement:
            case SyntaxKind.UsingStatement:
            case SyntaxKind.FixedStatement:
                // If we have something like this:
                //
                //    using (...)
                //    using (...)
                //    {
                //    }
                //
                // The first statement needs no block as it formatted with the same indentation.
                if (statement.Kind() == embeddedStatement.Kind())
                {
                    return;
                }

                break;
            }

            if (option.Value == PreferBracesPreference.WhenMultiline &&
                !IsConsideredMultiLine(statement, embeddedStatement) &&
                !RequiresBracesToMatchContext(statement))
            {
                return;
            }

            if (ContainsInterleavedDirective(statement, embeddedStatement, cancellationToken))
            {
                return;
            }

            var firstToken = statement.GetFirstToken();

            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         firstToken.GetLocation(),
                                         option.Notification.Severity,
                                         additionalLocations: null,
                                         properties: null,
                                         SyntaxFacts.GetText(firstToken.Kind())));
        }
Beispiel #26
0
        private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext, INamedTypeSymbol?expressionType)
        {
            var options           = syntaxContext.Options;
            var syntaxTree        = syntaxContext.Node.SyntaxTree;
            var cancellationToken = syntaxContext.CancellationToken;

            var styleOption = options.GetOption(CSharpCodeStyleOptions.PreferLocalOverAnonymousFunction, syntaxTree, cancellationToken);

            if (!styleOption.Value)
            {
                // Bail immediately if the user has disabled this feature.
                return;
            }

            var severity          = styleOption.Notification.Severity;
            var anonymousFunction = (AnonymousFunctionExpressionSyntax)syntaxContext.Node;

            var semanticModel = syntaxContext.SemanticModel;

            if (!CheckForPattern(anonymousFunction, out var localDeclaration))
            {
                return;
            }

            if (localDeclaration.Declaration.Variables.Count != 1)
            {
                return;
            }

            if (localDeclaration.Parent is not BlockSyntax block)
            {
                return;
            }

            // If there are compiler error on the declaration we can't reliably
            // tell that the refactoring will be accurate, so don't provide any
            // code diagnostics
            if (localDeclaration.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error))
            {
                return;
            }

            var local = semanticModel.GetDeclaredSymbol(localDeclaration.Declaration.Variables[0], cancellationToken);

            if (local == null)
            {
                return;
            }

            var delegateType = semanticModel.GetTypeInfo(anonymousFunction, cancellationToken).ConvertedType as INamedTypeSymbol;

            if (!delegateType.IsDelegateType() ||
                delegateType.DelegateInvokeMethod == null ||
                !CanReplaceDelegateWithLocalFunction(delegateType, localDeclaration, semanticModel, cancellationToken))
            {
                return;
            }

            if (!CanReplaceAnonymousWithLocalFunction(semanticModel, expressionType, local, block, anonymousFunction, out var referenceLocations, cancellationToken))
            {
                return;
            }

            // Looks good!
            var additionalLocations = ImmutableArray.Create(
                localDeclaration.GetLocation(),
                anonymousFunction.GetLocation());

            additionalLocations = additionalLocations.AddRange(referenceLocations);

            if (severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) < ReportDiagnostic.Hidden)
            {
                // If the diagnostic is not hidden, then just place the user visible part
                // on the local being initialized with the lambda.
                syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                                   Descriptor,
                                                   localDeclaration.Declaration.Variables[0].Identifier.GetLocation(),
                                                   severity,
                                                   additionalLocations,
                                                   properties: null));
            }
            else
            {
                // If the diagnostic is hidden, place it on the entire construct.
                syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                                   Descriptor,
                                                   localDeclaration.GetLocation(),
                                                   severity,
                                                   additionalLocations,
                                                   properties: null));

                var anonymousFunctionStatement = anonymousFunction.GetAncestor <StatementSyntax>();
                if (anonymousFunctionStatement != null && localDeclaration != anonymousFunctionStatement)
                {
                    syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                                       Descriptor,
                                                       anonymousFunctionStatement.GetLocation(),
                                                       severity,
                                                       additionalLocations,
                                                       properties: null));
                }
            }
        }
Beispiel #27
0
        private void AnalyzeInvokedMember(
            OperationAnalysisContext context,
            InfoCache infoCache,
            IOperation instance,
            IMethodSymbol targetMethodOpt,
            IOperation argumentValue,
            IPropertySymbol lengthLikePropertyOpt,
            CancellationToken cancellationToken
            )
        {
            // look for `s[s.Length - value]` or `s.Get(s.Length- value)`.

            // Needs to have the one arg for `s.Length - value`, and that arg needs to be
            // a subtraction.
            if (instance is null || !IsSubtraction(argumentValue, out var subtraction))
            {
                return;
            }

            if (!(subtraction.Syntax is BinaryExpressionSyntax binaryExpression))
            {
                return;
            }

            // Only supported on C# 8 and above.
            var syntaxTree   = binaryExpression.SyntaxTree;
            var parseOptions = (CSharpParseOptions)syntaxTree.Options;

            if (parseOptions.LanguageVersion < LanguageVersion.CSharp8)
            {
                return;
            }

            // Don't bother analyzing if the user doesn't like using Index/Range operators.
            var option = context.Options.GetOption(
                CSharpCodeStyleOptions.PreferIndexOperator,
                syntaxTree,
                cancellationToken
                );

            if (!option.Value)
            {
                return;
            }

            // Ok, looks promising.  We're indexing in with some subtraction expression. Examine the
            // type this indexer is in to see if there's another member that takes a System.Index
            // that we can convert to.
            //
            // Also ensure that the left side of the subtraction : `s.Length - value` is actually
            // getting the length off the same instance we're indexing into.

            lengthLikePropertyOpt ??= TryGetLengthLikeProperty(infoCache, targetMethodOpt);
            if (
                lengthLikePropertyOpt == null ||
                !IsInstanceLengthCheck(lengthLikePropertyOpt, instance, subtraction.LeftOperand)
                )
            {
                return;
            }

            // Everything looks good.  We can update this to use the System.Index member instead.
            context.ReportDiagnostic(
                DiagnosticHelper.Create(
                    Descriptor,
                    binaryExpression.GetLocation(),
                    option.Notification.Severity,
                    ImmutableArray <Location> .Empty,
                    ImmutableDictionary <string, string> .Empty
                    )
                );
        }
Beispiel #28
0
        private void AnalyzeCoalesceExpression(SyntaxNodeAnalysisContext context)
        {
            var cancellationToken = context.CancellationToken;
            var semanticModel     = context.SemanticModel;
            var options           = (CSharpParseOptions)semanticModel.SyntaxTree.Options;

            if (options.LanguageVersion < LanguageVersion.CSharp8)
            {
                return;
            }

            var coalesceExpression = (BinaryExpressionSyntax)context.Node;

            var option = context.GetOption(CodeStyleOptions2.PreferCompoundAssignment, coalesceExpression.Language);

            // Bail immediately if the user has disabled this feature.
            if (!option.Value)
            {
                return;
            }

            var coalesceLeft  = coalesceExpression.Left;
            var coalesceRight = coalesceExpression.Right;

            if (!(coalesceRight is ParenthesizedExpressionSyntax parenthesizedExpr))
            {
                return;
            }

            if (!(parenthesizedExpr.Expression is AssignmentExpressionSyntax assignment))
            {
                return;
            }

            if (assignment.Kind() != SyntaxKind.SimpleAssignmentExpression)
            {
                return;
            }

            // have    x ?? (y = z)
            // ensure that 'x' and 'y' are suitably equivalent.
            var syntaxFacts = CSharpSyntaxFacts.Instance;

            if (!syntaxFacts.AreEquivalent(coalesceLeft, assignment.Left))
            {
                return;
            }

            // Syntactically looks promising.  But we can only safely do this if 'expr'
            // is side-effect-free since we will be changing the number of times it is
            // executed from twice to once.
            if (!UseCompoundAssignmentUtilities.IsSideEffectFree(
                    syntaxFacts, coalesceLeft, semanticModel, cancellationToken))
            {
                return;
            }

            // Good match.
            context.ReportDiagnostic(DiagnosticHelper.Create(
                                         Descriptor,
                                         coalesceExpression.OperatorToken.GetLocation(),
                                         option.Notification.Severity,
                                         additionalLocations: ImmutableArray.Create(coalesceExpression.GetLocation()),
                                         properties: null));
        }
        protected bool TrySimplifyTypeNameExpression(SemanticModel model, SyntaxNode node, AnalyzerOptions analyzerOptions, out Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            diagnostic = default;

            var syntaxTree = node.SyntaxTree;
            var optionSet  = analyzerOptions.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return(false);
            }

            if (!CanSimplifyTypeNameExpressionCore(
                    model, node, optionSet,
                    out var issueSpan, out var diagnosticId, out var inDeclaration,
                    cancellationToken))
            {
                return(false);
            }

            if (model.SyntaxTree.OverlapsHiddenPosition(issueSpan, cancellationToken))
            {
                return(false);
            }

            PerLanguageOption <CodeStyleOption <bool> > option;
            DiagnosticDescriptor descriptor;
            ReportDiagnostic     severity;

            switch (diagnosticId)
            {
            case IDEDiagnosticIds.SimplifyNamesDiagnosticId:
                descriptor = s_descriptorSimplifyNames;
                severity   = descriptor.DefaultSeverity.ToReportDiagnostic();
                break;

            case IDEDiagnosticIds.SimplifyMemberAccessDiagnosticId:
                descriptor = s_descriptorSimplifyMemberAccess;
                severity   = descriptor.DefaultSeverity.ToReportDiagnostic();
                break;

            case IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId:
                option = inDeclaration
                        ? CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration
                        : CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess;
                descriptor = s_descriptorPreferBuiltinOrFrameworkType;

                var optionValue = optionSet.GetOption(option, GetLanguageName());
                severity = optionValue.Notification.Severity;
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(diagnosticId);
            }

            if (descriptor == null)
            {
                return(false);
            }

            var tree    = model.SyntaxTree;
            var builder = ImmutableDictionary.CreateBuilder <string, string>();

            builder["OptionName"]     = nameof(CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess); // TODO: need the actual one
            builder["OptionLanguage"] = model.Language;
            diagnostic = DiagnosticHelper.Create(descriptor, tree.GetLocation(issueSpan), severity, additionalLocations: null, builder.ToImmutable());

#if LOG
            var sourceText = tree.GetText(cancellationToken);
            sourceText.GetLineAndOffset(issueSpan.Start, out var startLineNumber, out var startOffset);
            sourceText.GetLineAndOffset(issueSpan.End, out var endLineNumber, out var endOffset);
            var logLine = tree.FilePath + "," + startLineNumber + "\t" + diagnosticId + "\t" + inDeclaration + "\t";

            var leading = sourceText.ToString(TextSpan.FromBounds(
                                                  sourceText.Lines[startLineNumber].Start, issueSpan.Start));
            var mid      = sourceText.ToString(issueSpan);
            var trailing = sourceText.ToString(TextSpan.FromBounds(
                                                   issueSpan.End, sourceText.Lines[endLineNumber].End));

            var contents = leading + "[|" + s_newlinePattern.Replace(mid, " ") + "|]" + trailing;
            logLine += contents + "\r\n";

            lock (_logGate)
            {
                File.AppendAllText(_logFile, logLine);
            }
#endif

            return(true);
        }
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
        {
            var conditionalExpression = (TConditionalExpressionSyntax)context.Node;

            var cancellationToken = context.CancellationToken;

            var option = context.GetOption(
                CodeStyleOptions2.PreferCoalesceExpression,
                conditionalExpression.Language
                );

            if (!option.Value)
            {
                return;
            }

            var syntaxFacts = GetSyntaxFacts();

            syntaxFacts.GetPartsOfConditionalExpression(
                conditionalExpression,
                out var conditionNode,
                out var whenTrueNodeHigh,
                out var whenFalseNodeHigh
                );

            conditionNode = syntaxFacts.WalkDownParentheses(conditionNode);
            var whenTrueNodeLow  = syntaxFacts.WalkDownParentheses(whenTrueNodeHigh);
            var whenFalseNodeLow = syntaxFacts.WalkDownParentheses(whenFalseNodeHigh);

            var notHasValueExpression = false;

            if (syntaxFacts.IsLogicalNotExpression(conditionNode))
            {
                notHasValueExpression = true;
                conditionNode         = syntaxFacts.GetOperandOfPrefixUnaryExpression(conditionNode);
            }

            if (!(conditionNode is TMemberAccessExpression conditionMemberAccess))
            {
                return;
            }

            syntaxFacts.GetPartsOfMemberAccessExpression(
                conditionMemberAccess,
                out var conditionExpression,
                out var conditionSimpleName
                );
            syntaxFacts.GetNameAndArityOfSimpleName(
                conditionSimpleName,
                out var conditionName,
                out _
                );

            if (conditionName != nameof(Nullable <int> .HasValue))
            {
                return;
            }

            var whenPartToCheck = notHasValueExpression ? whenFalseNodeLow : whenTrueNodeLow;

            if (!(whenPartToCheck is TMemberAccessExpression whenPartMemberAccess))
            {
                return;
            }

            syntaxFacts.GetPartsOfMemberAccessExpression(
                whenPartMemberAccess,
                out var whenPartExpression,
                out var whenPartSimpleName
                );
            syntaxFacts.GetNameAndArityOfSimpleName(
                whenPartSimpleName,
                out var whenPartName,
                out _
                );

            if (whenPartName != nameof(Nullable <int> .Value))
            {
                return;
            }

            if (!syntaxFacts.AreEquivalent(conditionExpression, whenPartExpression))
            {
                return;
            }

            // Syntactically this looks like something we can simplify.  Make sure we're
            // actually looking at something Nullable (and not some type that uses a similar
            // syntactic pattern).
            var semanticModel = context.SemanticModel;
            var nullableType  = semanticModel.Compilation.GetTypeByMetadataName(
                typeof(Nullable <>).FullName !
                );

            if (nullableType == null)
            {
                return;
            }

            var type = semanticModel.GetTypeInfo(conditionExpression, cancellationToken);

            if (!nullableType.Equals(type.Type?.OriginalDefinition))
            {
                return;
            }

            var whenPartToKeep = notHasValueExpression ? whenTrueNodeHigh : whenFalseNodeHigh;
            var locations      = ImmutableArray.Create(
                conditionalExpression.GetLocation(),
                conditionExpression.GetLocation(),
                whenPartToKeep.GetLocation()
                );

            context.ReportDiagnostic(
                DiagnosticHelper.Create(
                    Descriptor,
                    conditionalExpression.GetLocation(),
                    option.Notification.Severity,
                    locations,
                    properties: null
                    )
                );
        }