public static async Task ComputeRefactoringAsync(RefactoringContext context, MethodDeclarationSyntax methodDeclaration)
        {
            if (context.IsRefactoringEnabled(RefactoringIdentifiers.ChangeMethodReturnTypeToVoid) &&
                methodDeclaration.ReturnType?.IsVoid() == false &&
                methodDeclaration.Body?.Statements.Count > 0 &&
                !methodDeclaration.IsIterator() &&
                context.SupportsSemanticModel)
            {
                SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                if (!IsAsyncMethodThatReturnsTask(methodDeclaration, semanticModel, context.CancellationToken))
                {
                    ControlFlowAnalysis analysis = semanticModel.AnalyzeControlFlow(methodDeclaration.Body);

                    if (analysis.Succeeded &&
                        analysis.ReturnStatements.All(node => IsReturnStatementWithoutExpression(node)))
                    {
                        context.RegisterRefactoring(
                            "Change return type to 'void'",
                            cancellationToken =>
                        {
                            return(ChangeTypeRefactoring.ChangeTypeAsync(
                                       context.Document,
                                       methodDeclaration.ReturnType,
                                       CSharpFactory.VoidType(),
                                       cancellationToken));
                        });
                    }
                }
            }
        }
        private static IEnumerable <ITypeSymbol> AnalyzeTestCreationMethod(MethodDeclarationSyntax methodDeclaration, SemanticModel semanticModel)
        {
            var types = new HashSet <ITypeSymbol>();

            if (methodDeclaration.Body is null)
            {
                if (methodDeclaration.ExpressionBody?.Expression is ObjectCreationExpressionSyntax oces)
                {
                    var type = oces.GetTypeSymbol(semanticModel);
                    types.Add(type);
                }
            }
            else
            {
                var controlFlow      = semanticModel.AnalyzeControlFlow(methodDeclaration.Body);
                var returnStatements = controlFlow.ReturnStatements.OfType <ReturnStatementSyntax>();

                foreach (var variable in methodDeclaration.DescendantNodes().OfType <VariableDeclarationSyntax>().SelectMany(_ => _.Variables))
                {
                    if (returnStatements.Any(_ => _.Expression is IdentifierNameSyntax ins && variable.GetName() == ins.GetName()) && variable.Initializer?.Value is ObjectCreationExpressionSyntax oces)
                    {
                        var type = oces.GetTypeSymbol(semanticModel);
                        types.Add(type);
                    }
                }
            }

            return(types);
        }
示例#3
0
        static public MethodBlockAnalysis FromSemanticModel(MethodDeclarationSyntax methodSyntax, SemanticModel semanticModel)
        {
            var block       = SyntaxOperations.GetBodyOfMethod(methodSyntax).A;
            var controlFlow = semanticModel.AnalyzeControlFlow(block);
            var dataFlow    = semanticModel.AnalyzeDataFlow(block);

            return(new MethodBlockAnalysis(methodSyntax, block, semanticModel, controlFlow, dataFlow));
        }
            private bool ContainsReturnStatementInSelectedCode(SemanticModel model)
            {
                Contract.ThrowIfTrue(SelectionResult.SelectionInExpression);

                var pair = GetFlowAnalysisNodeRange();
                var controlFlowAnalysisData = model.AnalyzeControlFlow(pair.Item1, pair.Item2);

                return(ContainsReturnStatementInSelectedCode(controlFlowAnalysisData.ExitPoints));
            }
示例#5
0
        private static ControlFlowAnalysis?GetControlFlowAnalysis(SyntaxGenerator generator, SemanticModel semanticModel, SyntaxNode node)
        {
            var statements = generator.GetStatements(node);

            if (statements.Count > 0)
            {
                return(semanticModel.AnalyzeControlFlow(statements[0], statements[statements.Count - 1]));
            }

            return(null);
        }
        public static bool ReturnsNull(IEnumerable <StatementSyntax> statements, SemanticModel semanticModel)
        {
            var statementSyntaxes = statements as StatementSyntax[] ?? statements.ToArray();
            var returnStatements  = semanticModel.AnalyzeControlFlow(
                statementSyntaxes.First(),
                statementSyntaxes.Last())
                                    .ReturnStatements;

            return(returnStatements.Any(
                       stmt => stmt is ReturnStatementSyntax returnStatement &&
                       CanBeNull(returnStatement.Expression !, semanticModel)));
        }
            private bool IsEndOfSelectionReachable(SemanticModel model)
            {
                if (SelectionResult.SelectionInExpression)
                {
                    return(true);
                }

                var pair     = GetFlowAnalysisNodeRange();
                var analysis = model.AnalyzeControlFlow(pair.Item1, pair.Item2);

                return(analysis.EndPointIsReachable);
            }
        private static void AnalyzeControlFlow(
            SemanticModel semanticModel,
            StatementRange statementRange,
            out bool endPointIsReachable,
            out SyntaxNode?singleExitPoint)
        {
            var flow = semanticModel.AnalyzeControlFlow(
                statementRange.FirstStatement,
                statementRange.LastStatement);

            endPointIsReachable = flow.EndPointIsReachable;
            singleExitPoint     = flow.ExitPoints.Length == 1 ? flow.ExitPoints[0] : null;
        }
        public static List <INamedType> GetActualReturnTypes(this IMethodSymbol method, List <NamedTypeBase> types, SemanticModel model)
        {
            List <INamedType> returntypes = new List <INamedType>();

            foreach (var item in method.Locations)
            {
                if (item.IsInSource)
                {
                    var ms = model.SyntaxTree.GetRoot().FindNode(item.SourceSpan) as MethodDeclarationSyntax;
                    if (ms != null && ms.Body != null && ms.Body.Statements.Count != 0)
                    {
                        var cf = model.AnalyzeControlFlow(ms.Body.Statements.First(), ms.Body.Statements.Last());
                        foreach (var returnStatement in cf.ReturnStatements)
                        {
                            if (returnStatement is YieldStatementSyntax)
                            {
                                continue;
                            }

                            var ret = (ReturnStatementSyntax)returnStatement;
                            //void methods
                            if (ret.Expression != null && ret.Expression.CSharpKind() != SyntaxKind.NullLiteralExpression)
                            {
                                var type = model.GetTypeInfo(ret.Expression);
                                if (type.Type == null)
                                {
                                    logprovider.WriteToLog(string.Format("Cannot get semantinc type info for expression {0}", ret.Expression.ToString()));
                                    continue;
                                }
                                //hack
                                if (type.Type.TypeKind == TypeKind.TypeParameter)
                                {
                                    logprovider.WriteToLog(string.Format("Cannot get semantinc type info for expression {0}", ret.Expression.ToString()));
                                    continue;
                                }
                                var rtype = types.FirstOrDefault(t => t == ((ITypeSymbol)(type.Type)).ToCSharpNamedType(logprovider));
                                if (rtype != null)
                                {
                                    returntypes.Add(rtype);
                                }
                                else
                                {
                                    logprovider.WriteToLog(string.Format("Cannot find type for symbol {0}", type.Type.ToString()));
                                }
                            }
                        }
                    }
                }
            }
            return(returntypes);
        }
示例#10
0
        public static async Task ComputeRefactoringAsync(RefactoringContext context, MethodDeclarationSyntax methodDeclaration)
        {
            if (context.IsRefactoringEnabled(RefactoringIdentifiers.ChangeMethodReturnTypeToVoid))
            {
                TypeSyntax returnType = methodDeclaration.ReturnType;

                if (returnType?.IsVoid() == false)
                {
                    BlockSyntax body = methodDeclaration.Body;

                    if (body != null)
                    {
                        SyntaxList <StatementSyntax> statements = body.Statements;

                        if (statements.Any() &&
                            !ContainsOnlyThrowStatement(statements) &&
                            !methodDeclaration.ContainsYield())
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken);

                            if (methodSymbol?.IsOverride == false &&
                                !methodSymbol.ImplementsInterfaceMember() &&
                                !IsAsyncMethodThatReturnsTask(methodSymbol, semanticModel))
                            {
                                ControlFlowAnalysis analysis = semanticModel.AnalyzeControlFlow(body);

                                if (analysis.Succeeded &&
                                    analysis.ReturnStatements.All(IsReturnStatementWithoutExpression))
                                {
                                    context.RegisterRefactoring(
                                        "Change return type to 'void'",
                                        cancellationToken =>
                                    {
                                        return(ChangeTypeRefactoring.ChangeTypeAsync(
                                                   context.Document,
                                                   returnType,
                                                   CSharpFactory.VoidType(),
                                                   cancellationToken));
                                    });
                                }
                            }
                        }
                    }
                }
            }
        }
            private static bool IsEndPointReachable(SemanticModel semanticModel, StatementSyntax statementSyntax)
            {
                var result = semanticModel.AnalyzeControlFlow(statementSyntax);

                if (result == null || !result.Succeeded)
                {
                    return(false);
                }

                if (!result.EndPointIsReachable)
                {
                    return(false);
                }

                return(true);
            }
示例#12
0
        protected bool IsFinalSpanSemanticallyValidSpan(
            SemanticModel semanticModel, TextSpan textSpan, Tuple <SyntaxNode, SyntaxNode> range, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(range);

            var controlFlowAnalysisData = semanticModel.AnalyzeControlFlow(range.Item1, range.Item2);

            // there must be no control in and out of given span
            if (controlFlowAnalysisData.EntryPoints.Any())
            {
                return(false);
            }

            // check something like continue, break, yield break, yield return, and etc
            if (ContainsNonReturnExitPointsStatements(controlFlowAnalysisData.ExitPoints))
            {
                return(false);
            }

            // okay, there is no branch out, check whether next statement can be executed normally
            var returnStatements = GetOuterReturnStatements(range.Item1.GetCommonRoot(range.Item2), controlFlowAnalysisData.ExitPoints);

            if (!returnStatements.Any())
            {
                if (!controlFlowAnalysisData.EndPointIsReachable)
                {
                    // REVIEW: should we just do extract method regardless or show some warning to user?
                    // in dev10, looks like we went ahead and did the extract method even if selection contains
                    // unreachable code.
                }

                return(true);
            }

            // okay, only branch was return. make sure we have all return in the selection.

            // check for special case, if end point is not reachable, we don't care the selection
            // actually contains all return statements. we just let extract method go through
            // and work like we did in dev10
            if (!controlFlowAnalysisData.EndPointIsReachable)
            {
                return(true);
            }

            // there is a return statement, and current position is reachable. let's check whether this is a case where that is okay
            return(IsFinalSpanSemanticallyValidSpan(semanticModel.SyntaxTree.GetRoot(cancellationToken), textSpan, returnStatements, cancellationToken));
        }
        private static bool IsCalledFromAddSteps(InvocationExpressionSyntax invocationExpressionNode, SemanticModel semanticModel)
        {
            var containingMethodSyntax = invocationExpressionNode.FirstAncestorOrSelf<MemberDeclarationSyntax>();

            var blockSyntax = (BlockSyntax)containingMethodSyntax.ChildNodes().Last();

            var controlFlow = semanticModel.AnalyzeControlFlow(blockSyntax.Statements.First(), blockSyntax.Statements.Last());

            if (controlFlow.Succeeded)
            {

            }

            var token = containingMethodSyntax.DescendantTokens().FirstOrDefault(f => f.IsKind(SyntaxKind.IdentifierToken));

            return token.Text == "AddSteps";
        }
            public bool CatchClauseCatchesCoroutineStopped(CatchClauseSyntax catchClause,
                                                           SemanticModel model, out bool rethrows)
            {
                rethrows = false;
                if (catchClause.Declaration != null)
                {
                    INamedTypeSymbol exceptionSymbol =
                        model.GetSymbolInfo(catchClause.Declaration.Type).Symbol as
                        INamedTypeSymbol;

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

                    if (exceptionSymbol != ExceptionType &&
                        exceptionSymbol != CoroutineStoppedExceptionType)
                    {
                        return(false);
                    }

                    if (RethrowsCoroutineStoppedException(catchClause, model))
                    {
                        rethrows = true;
                        return(true);
                    }
                }

                ControlFlowAnalysis controlFlowResult =
                    model.AnalyzeControlFlow(catchClause.Block);

                // Check if this catch always rethrows
                if (controlFlowResult.Succeeded &&
                    !controlFlowResult.EndPointIsReachable &&
                    controlFlowResult.ExitPoints.Length == 0 &&
                    controlFlowResult.ReturnStatements.Length == 0)
                {
                    rethrows = true;
                }

                return(true);
            }
        private static void ComputeRefactoring(
            RefactoringContext context,
            IMethodSymbol methodSymbol,
            SemanticModel semanticModel,
            BlockSyntax body,
            TypeSyntax returnType)
        {
            if (methodSymbol?.IsOverride != false)
            {
                return;
            }

            if (methodSymbol.ImplementsInterfaceMember())
            {
                return;
            }

            if (methodSymbol.IsAsync &&
                methodSymbol.ReturnType.HasMetadataName(MetadataNames.System_Threading_Tasks_Task))
            {
                return;
            }

            ControlFlowAnalysis analysis = semanticModel.AnalyzeControlFlow(body);

            if (!analysis.Succeeded)
            {
                return;
            }

            if (!analysis.ReturnStatements.All(f => (f as ReturnStatementSyntax)?.Expression == null))
            {
                return;
            }

            Document document = context.Document;

            context.RegisterRefactoring(
                "Change return type to 'void'",
                ct => document.ReplaceNodeAsync(returnType, CSharpFactory.VoidType().WithTriviaFrom(returnType), ct),
                RefactoringDescriptors.ChangeMethodReturnTypeToVoid);
        }
        static void CollectSwitchSectionStatements(List <StatementSyntax> result, SemanticModel context,
                                                   StatementSyntax statement)
        {
            var blockStatement = statement as BlockSyntax;

            if (blockStatement != null)
            {
                result.AddRange(blockStatement.Statements);
            }
            else
            {
                result.Add(statement);
            }

            // add 'break;' at end if necessary
            var reachabilityAnalysis = context.AnalyzeControlFlow(statement);

            if (reachabilityAnalysis.EndPointIsReachable)
            {
                result.Add(SyntaxFactory.BreakStatement());
            }
        }
        public override SyntaxNode VisitBlock(BlockSyntax node)
        {
            SyntaxList <StatementSyntax> statements = node.Statements;

            // eliminate unreachable statements (cosmetic)
            List <int> remove = new List <int>();

            for (int i = 0; i < statements.Count; i++)
            {
                try
                {
                    ControlFlowAnalysis cfa = semanticModel.AnalyzeControlFlow(statements[i]);
                    if (!cfa.StartPointIsReachable)
                    {
                        remove.Add(i);
                    }
                }
                catch (ArgumentException)
                {
                    // probably node not in tree (because we rewrote it)
                    // do nothing and come back next time
                    Debug.Assert(Changed);
                    break;
                }
            }
            if (remove.Count != 0)
            {
                Changed = true;
                for (int i = remove.Count - 1; i >= 0; i--)
                {
                    statements = statements.RemoveAt(remove[i]);
                }
                node = node.WithStatements(statements);
            }

            return(base.VisitBlock(node));
        }
        // Ensure that all usages of the pattern variable are
        // in scope and definitely assigned after replacement.
        private static bool CheckDefiniteAssignment(
            SemanticModel semanticModel,
            ISymbol localVariable,
            StatementSyntax declarationStatement,
            StatementSyntax targetStatement,
            BlockSyntax parentBlock,
            bool isNegativeNullCheck)
        {
            var statements       = parentBlock.Statements;
            var targetIndex      = statements.IndexOf(targetStatement);
            var declarationIndex = statements.IndexOf(declarationStatement);

            // Since we're going to remove this declaration-statement,
            // we need to first ensure that it's not used up to the target statement.
            if (declarationIndex + 1 < targetIndex)
            {
                var dataFlow = semanticModel.AnalyzeDataFlow(
                    statements[declarationIndex + 1],
                    statements[targetIndex - 1]);

                if (dataFlow.ReadInside.Contains(localVariable) ||
                    dataFlow.WrittenInside.Contains(localVariable))
                {
                    return(false);
                }
            }

            // In case of an if-statement, we need to check if the variable
            // is being accessed before assignment in the opposite branch.
            if (targetStatement.IsKind(SyntaxKind.IfStatement, out IfStatementSyntax ifStatement))
            {
                var statement = isNegativeNullCheck ? ifStatement.Statement : ifStatement.Else?.Statement;
                if (statement != null)
                {
                    var dataFlow = semanticModel.AnalyzeDataFlow(statement);
                    if (dataFlow.DataFlowsIn.Contains(localVariable))
                    {
                        // Access before assignment is not safe in the opposite branch
                        // as the variable is not definitely assgined at this point.
                        // For example:
                        //
                        //    if (o is string x) { }
                        //    else { Use(x); }
                        //
                        return(false);
                    }

                    if (dataFlow.AlwaysAssigned.Contains(localVariable))
                    {
                        // If the variable is always assigned here, we don't need to check
                        // subsequent statements as it's definitely assigned afterwards.
                        // For example:
                        //
                        //     if (o is string x) { }
                        //     else { x = null; }
                        //
                        return(true);
                    }
                }
            }

            // Make sure that no access is made to the variable before assignment in the subsequent statements
            if (targetIndex + 1 < statements.Count)
            {
                var dataFlow = semanticModel.AnalyzeDataFlow(
                    statements[targetIndex + 1],
                    statements[statements.Count - 1]);

                if (targetStatement.Kind() == SyntaxKind.WhileStatement)
                {
                    // The scope of pattern variables in a while-statement does not leak out to
                    // the enclosing block so we bail also if there is any assignments afterwards.
                    if (dataFlow.ReadInside.Contains(localVariable) ||
                        dataFlow.WrittenInside.Contains(localVariable))
                    {
                        return(false);
                    }
                }
                else if (dataFlow.DataFlowsIn.Contains(localVariable))
                {
                    // Access before assignment here is only valid if we have a negative
                    // pattern-matching in an if-statement with an unreachable endpoint.
                    // For example:
                    //
                    //      if (!(o is string x)) {
                    //        return;
                    //      }
                    //
                    //      // The 'return' statement above ensures x is definitely assigned here
                    //      Console.WriteLine(x);
                    //
                    return(isNegativeNullCheck &&
                           ifStatement != null &&
                           !semanticModel.AnalyzeControlFlow(ifStatement.Statement).EndPointIsReachable);
                }
            }

            return(true);
        }
示例#19
0
        private bool RequiresFix(SemanticModel semanticModel, StatementSyntax statementSyntax)
        {
            var controlFlow = semanticModel.AnalyzeControlFlow(statementSyntax);

            return(controlFlow.ExitPoints.Length == 0);
        }
示例#20
0
        private bool ContainsReturnStatementInSelectedCode(SemanticModel model)
        {
            var dataFlowAnalysis = _semanticModel.AnalyzeControlFlow(_selectionResult.FirstStatement(), _selectionResult.LastStatement());

            return(ContainsReturnStatementInSelectedCode(dataFlowAnalysis.ExitPoints));
        }
            // To convert a null-check to pattern-matching, we should make sure of a few things:
            //
            //      (1) The pattern variable may not be used before the point of declaration.
            //
            //          {
            //              var use = t;
            //              if (x is T t) {}
            //          }
            //
            //      (2) The pattern variable may not be used outside of the new scope which
            //          is determined by the parent statement.
            //
            //          {
            //              if (x is T t) {}
            //          }
            //
            //          var use = t;
            //
            //      (3) The pattern variable may not be used before assignment in opposite
            //          branches, if any.
            //
            //          {
            //              if (x is T t) {}
            //              var use = t;
            //          }
            //
            // We walk up the tree from the point of null-check and see if any of the above is violated.
            private bool CanSafelyConvertToPatternMatching()
            {
                // Keep track of whether the pattern variable is definitely assigned when false/true.
                // We start by the null-check itself, if it's compared with '==', the pattern variable
                // will be definitely assigned when false, because we wrap the is-operator in a !-operator.
                var defAssignedWhenTrue = _comparison.Kind() == SyntaxKind.NotEqualsExpression;

                foreach (var current in _comparison.Ancestors())
                {
                    // Checking for any conditional statement or expression that could possibly
                    // affect or determine the state of definite-assignment of the pattern variable.
                    switch (current.Kind())
                    {
                    case SyntaxKind.LogicalAndExpression when !defAssignedWhenTrue:
                    case SyntaxKind.LogicalOrExpression when defAssignedWhenTrue:
                        // Since the pattern variable is only definitely assigned if the pattern
                        // succeeded, in the following cases it would not be safe to use pattern-matching.
                        // For example:
                        //
                        //      if ((x = o as string) == null && SomeExpression)
                        //      if ((x = o as string) != null || SomeExpression)
                        //
                        // Here, x would never be definitely assigned if pattern-matching were used.
                        return(false);

                    case SyntaxKind.LogicalAndExpression:
                    case SyntaxKind.LogicalOrExpression:

                    // Parentheses and cast expressions do not contribute to the flow analysis.
                    case SyntaxKind.ParenthesizedExpression:
                    case SyntaxKind.CastExpression:

                    // Skip over declaration parts to get to the parenting statement
                    // which might be a for-statement or a local declaration statement.
                    case SyntaxKind.EqualsValueClause:
                    case SyntaxKind.VariableDeclarator:
                    case SyntaxKind.VariableDeclaration:
                        continue;

                    case SyntaxKind.LogicalNotExpression:
                        // The !-operator negates the definitive assignment state.
                        defAssignedWhenTrue = !defAssignedWhenTrue;
                        continue;

                    case SyntaxKind.ConditionalExpression:
                        var conditionalExpression = (ConditionalExpressionSyntax)current;
                        if (LocalFlowsIn(defAssignedWhenTrue
                                    ? conditionalExpression.WhenFalse
                                    : conditionalExpression.WhenTrue))
                        {
                            // In a conditional expression, the pattern variable
                            // would not be definitely assigned in the opposite branch.
                            return(false);
                        }

                        return(CheckExpression(conditionalExpression));

                    case SyntaxKind.ForStatement:
                        var forStatement = (ForStatementSyntax)current;
                        if (!forStatement.Condition.Span.Contains(_comparison.Span))
                        {
                            // In a for-statement, only the condition expression
                            // can make this definitely assigned in the loop body.
                            return(false);
                        }

                        return(CheckLoop(forStatement, forStatement.Statement, defAssignedWhenTrue));

                    case SyntaxKind.WhileStatement:
                        var whileStatement = (WhileStatementSyntax)current;
                        return(CheckLoop(whileStatement, whileStatement.Statement, defAssignedWhenTrue));

                    case SyntaxKind.IfStatement:
                        var ifStatement       = (IfStatementSyntax)current;
                        var oppositeStatement = defAssignedWhenTrue
                                ? ifStatement.Else?.Statement
                                : ifStatement.Statement;

                        if (oppositeStatement != null)
                        {
                            var dataFlow = _semanticModel.AnalyzeDataFlow(oppositeStatement);
                            if (dataFlow.DataFlowsIn.Contains(_localSymbol))
                            {
                                // Access before assignment is not safe in the opposite branch
                                // as the variable is not definitely assgined at this point.
                                // For example:
                                //
                                //    if (o is string x) { }
                                //    else { Use(x); }
                                //
                                return(false);
                            }

                            if (dataFlow.AlwaysAssigned.Contains(_localSymbol))
                            {
                                // If the variable is always assigned here, we don't need to check
                                // subsequent statements as it's definitely assigned afterwards.
                                // For example:
                                //
                                //     if (o is string x) { }
                                //     else { x = null; }
                                //
                                return(true);
                            }
                        }

                        if (!defAssignedWhenTrue &&
                            !_semanticModel.AnalyzeControlFlow(ifStatement.Statement).EndPointIsReachable)
                        {
                            // Access before assignment here is only valid if we have a negative
                            // pattern-matching in an if-statement with an unreachable endpoint.
                            // For example:
                            //
                            //      if (!(o is string x)) {
                            //        return;
                            //      }
                            //
                            //      // The 'return' statement above ensures x is definitely assigned here
                            //      Console.WriteLine(x);
                            //
                            return(true);
                        }

                        return(CheckStatement(ifStatement));
                    }

                    switch (current)
                    {
                    case ExpressionSyntax expression:
                        // If we reached here, it means we have a sub-expression that
                        // does not garantee definite assignment. We should make sure that
                        // the pattern variable is not used outside of the expression boundaries.
                        return(CheckExpression(expression));

                    case StatementSyntax statement:
                        // If we reached here, it means that the null-check is appeared in
                        // a statement. In that case, the variable would be actually in the
                        // scope in subsequent statements, but not definitely assigned.
                        // Therefore, we should ensure that there is no use before assignment.
                        return(CheckStatement(statement));
                    }

                    // Bail out for error cases and unhandled cases.
                    break;
                }

                return(false);
            }
        public static async Task ComputeRefactoringAsync(RefactoringContext context, MethodDeclarationSyntax methodDeclaration)
        {
            TypeSyntax returnType = methodDeclaration.ReturnType;

            if (returnType?.IsVoid() != false)
            {
                return;
            }

            BlockSyntax body = methodDeclaration.Body;

            if (body == null)
            {
                return;
            }

            SyntaxList <StatementSyntax> statements = body.Statements;

            if (!statements.Any())
            {
                return;
            }

            if (statements.SingleOrDefault(shouldThrow: false)?.Kind() == SyntaxKind.ThrowStatement)
            {
                return;
            }

            if (methodDeclaration.ContainsYield())
            {
                return;
            }

            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

            IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken);

            if (methodSymbol?.IsOverride != false)
            {
                return;
            }

            if (methodSymbol.ImplementsInterfaceMember())
            {
                return;
            }

            if (IsAsyncMethodThatReturnsTask(methodSymbol, semanticModel))
            {
                return;
            }

            ControlFlowAnalysis analysis = semanticModel.AnalyzeControlFlow(body);

            if (!analysis.Succeeded)
            {
                return;
            }

            if (!analysis.ReturnStatements.All(IsReturnStatementWithoutExpression))
            {
                return;
            }

            context.RegisterRefactoring(
                "Change return type to 'void'",
                ct => ChangeTypeRefactoring.ChangeTypeAsync(context.Document, returnType, CSharpFactory.VoidType(), ct),
                RefactoringIdentifiers.ChangeMethodReturnTypeToVoid);
        }