public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, _, cancellationToken) = context; var calleeInvocationNode = await context.TryGetRelevantNodeAsync <TInvocationSyntax>().ConfigureAwait(false); if (calleeInvocationNode == null) { return; } var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var calleeMethodSymbol = semanticModel.GetSymbolInfo(calleeInvocationNode, cancellationToken).GetAnySymbol() as IMethodSymbol; if (calleeMethodSymbol == null) { return; } if (calleeMethodSymbol.PartialImplementationPart != null) { calleeMethodSymbol = calleeMethodSymbol.PartialImplementationPart; } if (!calleeMethodSymbol.IsOrdinaryMethod() && !calleeMethodSymbol.IsExtensionMethod) { return; } if (calleeMethodSymbol.IsVararg) { return; } if (calleeMethodSymbol.DeclaredAccessibility != Accessibility.Private) { return; } var symbolDeclarationService = document.GetRequiredLanguageService <ISymbolDeclarationService>(); var calleeMethodDeclarationSyntaxReferences = symbolDeclarationService.GetDeclarations(calleeMethodSymbol); if (calleeMethodDeclarationSyntaxReferences.Length != 1) { return; } var calleeMethodDeclarationSyntaxReference = calleeMethodDeclarationSyntaxReferences[0]; var calleeMethodNode = await calleeMethodDeclarationSyntaxReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false) as TMethodDeclarationSyntax; if (calleeMethodNode == null) { return; } var inlineExpression = GetRawInlineExpression(calleeMethodNode); // Special case 1: AwaitExpression if (_syntaxFacts.IsAwaitExpression(inlineExpression)) { // 1. If Caller & callee both have 'await' make sure there is no duplicate 'await' // Example: // Before: // async Task Caller() => await Callee(); // async Task Callee() => await Task.CompletedTask; // After: // async Task Caller() => await Task.CompletedTask; // async Task Callee() => await Task.CompletedTask; // The original inline expression in callee will be 'await Task.CompletedTask' // The caller just need 'Task.CompletedTask' without the 'await' // // 2. If Caller doesn't have await but callee has. // Example: // Before: // void Caller() { Callee().Wait();} // async Task Callee() => await DoAsync(); // After: // void Caller() { DoAsync().Wait(); } // async Task Callee() => await DoAsync(); // What caller is expecting is an expression returns 'Task', which doesn't include the 'await' inlineExpression = _syntaxFacts.GetExpressionOfAwaitExpression(inlineExpression) as TExpressionSyntax; } if (inlineExpression == null) { return; } // Special case 2: ThrowStatement & ThrowExpresion if (_syntaxFacts.IsThrowStatement(inlineExpression.Parent) || _syntaxFacts.IsThrowExpression(inlineExpression)) { // If this is a throw statement, then it should be valid for // 1. If it is invoked as ExpressionStatement // Example: // Before: // void Caller() { Callee(); } // void Callee() { throw new Exception();} // After: // void Caller() { throw new Exception(); } // void Callee() { throw new Exception();} // 2. If it is invoked in a place allow throw expression // Example: // Before: // void Caller(bool flag) { var x = flag ? Callee() : 1; } // int Callee() { throw new Exception();} // After: // void Caller() { var x = flag ? throw new Exception() : 1; } // int Callee() { throw new Exception();} // Note here throw statement is changed to throw expression after inlining // If this is a throw expression, the check is the same // 1. If it is invoked as ExpressionStatement // Example: // Before: // void Caller() { Callee(); } // void Callee() => throw new Exception(); // After: // void Caller() { throw new Exception(); } // void Callee() => throw new Exception(); // Note here throw expression is converted to throw statement // 2. If it is invoked in a place allow throw expression // Example: // Before: // void Caller(bool flag) { var x = flag ? Callee() : 1; } // int Callee() => throw new Exception(); // After: // void Caller() { var x = flag ? throw new Exception() : 1; } // int Callee() => throw new Exception(); if (!CanBeReplacedByThrowExpression(calleeInvocationNode) && !_syntaxFacts.IsExpressionStatement(calleeInvocationNode.Parent)) { return; } } var callerSymbol = GetCallerSymbol(calleeInvocationNode, semanticModel, cancellationToken); if (callerSymbol == null) { return; } var callerReferences = symbolDeclarationService.GetDeclarations(callerSymbol); if (callerReferences.Length != 1) { return; } var callerDeclarationNode = await callerReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); var invocationOperation = semanticModel.GetOperation(calleeInvocationNode, cancellationToken) as IInvocationOperation; if (invocationOperation == null) { return; } var codeActions = GenerateCodeActions( document, calleeInvocationNode, calleeMethodSymbol, calleeMethodNode, callerSymbol, callerDeclarationNode, inlineExpression, invocationOperation); var nestedCodeAction = CodeAction.CodeActionWithNestedActions.Create( string.Format(FeaturesResources.Inline_0, calleeMethodSymbol.ToNameDisplayString()), codeActions, isInlinable: true); context.RegisterRefactoring(nestedCodeAction, calleeInvocationNode.Span); }