Beispiel #1
0
 public GenerateLocalCodeAction(TService service, Document document, State state, CodeGenerationOptionsProvider fallbackOptions)
 {
     _service         = service;
     _document        = document;
     _state           = state;
     _fallbackOptions = fallbackOptions;
 }
        private static async Task <Document> UpdateDocumentAsync(
            SyntaxNode root,
            Document document,
            BlockSyntax parentBlock,
            LocalFunctionStatementSyntax localFunction,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var declaredSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(localFunction, cancellationToken);

            var dataFlow = semanticModel.AnalyzeDataFlow(
                localFunction.Body ?? (SyntaxNode)localFunction.ExpressionBody.Expression);

            // Exclude local function parameters in case they were captured inside the function body
            var captures = dataFlow.CapturedInside.Except(dataFlow.VariablesDeclared).Except(declaredSymbol.Parameters).ToList();

            // First, create a parameter per each capture so that we can pass them as arguments to the final method
            // Filter out `this` because it doesn't need a parameter, we will just make a non-static method for that
            // We also make a `ref` parameter here for each capture that is being written into inside the function
            var capturesAsParameters = captures
                                       .Where(capture => !capture.IsThisParameter())
                                       .Select(capture => CodeGenerationSymbolFactory.CreateParameterSymbol(
                                                   attributes: default,
        public override async Task <ImmutableArray <SyntaxNode> > GetReplacementMembersAsync(
            Document document,
            IPropertySymbol property,
            SyntaxNode propertyDeclarationNode,
            IFieldSymbol propertyBackingField,
            string desiredGetMethodName,
            string desiredSetMethodName,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            if (propertyDeclarationNode is not PropertyDeclarationSyntax propertyDeclaration)
            {
                return(ImmutableArray <SyntaxNode> .Empty);
            }

            var options = (CSharpCodeGenerationOptions)await document.GetCodeGenerationOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false);

            var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var languageVersion = syntaxTree.Options.LanguageVersion();

            return(ConvertPropertyToMembers(
                       languageVersion,
                       SyntaxGenerator.GetGenerator(document), property,
                       propertyDeclaration, propertyBackingField,
                       options.PreferExpressionBodiedMethods.Value, desiredGetMethodName, desiredSetMethodName,
                       cancellationToken));
        }
        private static async Task <AddConstructorParameterResult?> AddConstructorParametersFromMembersAsync(
            Document document, TextSpan textSpan, CodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Refactoring_GenerateFromMembers_AddConstructorParametersFromMembers, cancellationToken))
            {
                var info = await GetSelectedMemberInfoAsync(
                    document,
                    textSpan,
                    allowPartialSelection : true,
                    cancellationToken).ConfigureAwait(false);

                if (info != null)
                {
                    var state = await State.GenerateAsync(info.SelectedMembers, document, fallbackOptions, cancellationToken).ConfigureAwait(false);

                    if (state?.ConstructorCandidates != null && !state.ConstructorCandidates.IsEmpty)
                    {
                        var codeGenOptions = await document.GetCodeGenerationOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false);

                        var contextInfo = codeGenOptions.GetInfo(CodeGenerationContext.Default, document.Project);

                        return(CreateCodeActions(document, contextInfo, state));
                    }
                }

                return(null);
            }
        }
Beispiel #5
0
        protected override async Task <ImmutableArray <CodeAction> > GetRefactoringsForSingleParameterAsync(
            Document document,
            TParameterSyntax parameterSyntax,
            IParameterSymbol parameter,
            SyntaxNode constructorDeclaration,
            IMethodSymbol method,
            IBlockOperation?blockStatementOpt,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            // Only supported for constructor parameters.
            if (method.MethodKind != MethodKind.Constructor)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            var typeDeclaration = constructorDeclaration.GetAncestor <TTypeDeclarationSyntax>();

            if (typeDeclaration == null)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            // See if we're already assigning this parameter to a field/property in this type. If so, there's nothing
            // more for us to do.
            var assignmentStatement = TryFindFieldOrPropertyAssignmentStatement(parameter, blockStatementOpt);

            if (assignmentStatement != null)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            // Haven't initialized any fields/properties with this parameter.  Offer to assign
            // to an existing matching field/prop if we can find one, or add a new field/prop
            // if we can't.

            var rules = await document.GetNamingRulesAsync(cancellationToken).ConfigureAwait(false);

            var parameterNameParts = IdentifierNameParts.CreateIdentifierNameParts(parameter, rules);

            if (parameterNameParts.BaseName == "")
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            var fieldOrProperty = await TryFindMatchingUninitializedFieldOrPropertySymbolAsync(
                document, parameter, blockStatementOpt, rules, parameterNameParts.BaseNameParts, cancellationToken).ConfigureAwait(false);

            if (fieldOrProperty != null)
            {
                return(HandleExistingFieldOrProperty(
                           document, parameter, constructorDeclaration,
                           blockStatementOpt, fieldOrProperty, fallbackOptions));
            }

            return(await HandleNoExistingFieldOrPropertyAsync(
                       document, parameter, constructorDeclaration,
                       method, blockStatementOpt, rules, fallbackOptions, cancellationToken).ConfigureAwait(false));
        }
 protected abstract Task <ImmutableArray <CodeAction> > GetRefactoringsForSingleParameterAsync(
     Document document,
     TParameterSyntax parameterSyntax,
     IParameterSymbol parameter,
     SyntaxNode functionDeclaration,
     IMethodSymbol methodSymbol,
     IBlockOperation?blockStatementOpt,
     CodeGenerationOptionsProvider fallbackOptions,
     CancellationToken cancellationToken);
        private static async Task AddParameterCodeActionsAsync(
            ArrayBuilder <CodeAction> result, Document document, State state, CodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken)
        {
            if (state.CanGenerateParameter())
            {
                // Don't generate parameters with a `_` prefix unless that's what the user really wants as their naming style.
                if (await NameIsHighlyUnlikelyToWarrantSymbolAsync(
                        document, state, SymbolKind.Parameter, Accessibility.NotApplicable, fallbackOptions, cancellationToken).ConfigureAwait(false))
                {
                    return;
                }

                var containingMethod = state.ContainingMethod;
                var parameterIndex   = containingMethod.Parameters.Length;

                if (containingMethod.Parameters.Length > 0)
                {
                    var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);

                    var cancellationTokenType = compilation.CancellationTokenType();

                    for (var i = containingMethod.Parameters.Length - 1; i >= 0; i--)
                    {
                        var parameter = containingMethod.Parameters[i];

                        // Keep moving the insertion position for the generated parameter backwards
                        // until we get to a parameter that does not need to be at the end of the
                        // parameter list.
                        if (parameter.HasExplicitDefaultValue ||
                            parameter.IsParams ||
                            parameter.RefKind is RefKind.Out ||
                            Equals(parameter.Type, cancellationTokenType))
                        {
                            parameterIndex = i;
                            continue;
                        }

                        break;
                    }

                    // If we are in an extension method, then we want to make sure to insert after
                    // the first parameter.
                    if (containingMethod.IsExtensionMethod && parameterIndex == 0)
                    {
                        parameterIndex = 1;
                    }
                }

                result.Add(new GenerateParameterCodeAction(document, state, includeOverridesAndImplementations: false, parameterIndex));

                if (AddParameterService.HasCascadingDeclarations(state.ContainingMethod))
                {
                    result.Add(new GenerateParameterCodeAction(document, state, includeOverridesAndImplementations: true, parameterIndex));
                }
            }
        }
Beispiel #8
0
        private async Task <ImmutableArray <CodeAction> > HandleNoExistingFieldOrPropertyAsync(
            Document document,
            IParameterSymbol parameter,
            SyntaxNode constructorDeclaration,
            IMethodSymbol method,
            IBlockOperation?blockStatementOpt,
            ImmutableArray <NamingRule> rules,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            // Didn't find a field/prop that this parameter could be assigned to.
            // Offer to create new one and assign to that.
            using var _ = ArrayBuilder <CodeAction> .GetInstance(out var allActions);

            var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

            var(fieldAction, propertyAction) = AddSpecificParameterInitializationActions(
                document, parameter, constructorDeclaration, blockStatementOpt, rules, options, fallbackOptions);

            // Check if the surrounding parameters are assigned to another field in this class.  If so, offer to
            // make this parameter into a field as well.  Otherwise, default to generating a property
            var siblingFieldOrProperty = TryFindSiblingFieldOrProperty(parameter, blockStatementOpt);

            if (siblingFieldOrProperty is IFieldSymbol)
            {
                allActions.Add(fieldAction);
                allActions.Add(propertyAction);
            }
            else
            {
                allActions.Add(propertyAction);
                allActions.Add(fieldAction);
            }

            var(allFieldsAction, allPropertiesAction) = AddAllParameterInitializationActions(
                document, constructorDeclaration, method, blockStatementOpt, rules, options, fallbackOptions);

            if (allFieldsAction != null && allPropertiesAction != null)
            {
                if (siblingFieldOrProperty is IFieldSymbol)
                {
                    allActions.Add(allFieldsAction);
                    allActions.Add(allPropertiesAction);
                }
                else
                {
                    allActions.Add(allPropertiesAction);
                    allActions.Add(allFieldsAction);
                }
            }

            return(allActions.ToImmutable());
        }
 private (CodeAction?fieldAction, CodeAction?propertyAction) AddAllParameterInitializationActions(
     Document document,
     SyntaxNode constructorDeclaration,
     IMethodSymbol method,
     IBlockOperation?blockStatementOpt,
     ImmutableArray <NamingRule> rules,
     AccessibilityModifiersRequired accessibilityModifiersRequired,
     CodeGenerationOptionsProvider fallbackOptions)
 {
     if (blockStatementOpt == null)
     {
         return(default);
        public static async Task <Document> MakeLocalFunctionStaticAsync(
            Document document,
            LocalFunctionStatementSyntax localFunction,
            ImmutableArray <ISymbol> captures,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            var root         = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)) !;
            var syntaxEditor = new SyntaxEditor(root, document.Project.Solution.Services);

            await MakeLocalFunctionStaticAsync(document, localFunction, captures, syntaxEditor, fallbackOptions, cancellationToken).ConfigureAwait(false);

            return(document.WithSyntaxRoot(syntaxEditor.GetChangedRoot()));
        }
        private async Task AddLocalCodeActionsAsync(
            ArrayBuilder <CodeAction> result, Document document, State state, CodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken)
        {
            if (state.CanGenerateLocal())
            {
                // Don't generate locals with a `_` prefix unless that's what the user really wants as their naming style.
                if (await NameIsHighlyUnlikelyToWarrantSymbolAsync(
                        document, state, SymbolKind.Local, Accessibility.NotApplicable, fallbackOptions, cancellationToken).ConfigureAwait(false))
                {
                    return;
                }

                result.Add(new GenerateLocalCodeAction((TService)this, document, state, fallbackOptions));
            }
        }
Beispiel #12
0
        protected override async Task <ImmutableArray <CodeAction> > GetRefactoringsForSingleParameterAsync(
            Document document,
            TParameterSyntax parameterSyntax,
            IParameterSymbol parameter,
            SyntaxNode funcOrRecord,
            IMethodSymbol methodSymbol,
            IBlockOperation?blockStatementOpt,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // Only should provide null-checks for reference types and nullable types.
            if (!ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            // Great.  There was no null check.  Offer to add one.
            using var result = TemporaryArray <CodeAction> .Empty;
            result.Add(CodeAction.Create(
                           FeaturesResources.Add_null_check,
                           c => AddNullCheckAsync(document, parameterSyntax, parameter, funcOrRecord, methodSymbol, blockStatementOpt, c),
                           nameof(FeaturesResources.Add_null_check)));

            // Also, if this was a string, offer to add the special checks to string.IsNullOrEmpty and
            // string.IsNullOrWhitespace.  We cannot do this for records though as they have no location
            // to place the checks.
            if (parameter.Type.SpecialType == SpecialType.System_String && !IsRecordDeclaration(funcOrRecord))
            {
                result.Add(CodeAction.Create(
                               FeaturesResources.Add_string_IsNullOrEmpty_check,
                               c => AddStringCheckAsync(document, parameter, funcOrRecord, methodSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), c),
                               nameof(FeaturesResources.Add_string_IsNullOrEmpty_check)));

                result.Add(CodeAction.Create(
                               FeaturesResources.Add_string_IsNullOrWhiteSpace_check,
                               c => AddStringCheckAsync(document, parameter, funcOrRecord, methodSymbol, blockStatementOpt, nameof(string.IsNullOrWhiteSpace), c),
                               nameof(FeaturesResources.Add_string_IsNullOrWhiteSpace_check)));
            }

            return(result.ToImmutableAndClear());
        }
Beispiel #13
0
 public IntroduceParameterDocumentRewriter(
     AbstractIntroduceParameterService <TExpressionSyntax, TInvocationExpressionSyntax, TObjectCreationExpressionSyntax, TIdentifierNameSyntax> service,
     Document originalDocument,
     TExpressionSyntax expression,
     IMethodSymbol methodSymbol,
     SyntaxNode containingMethod,
     IntroduceParameterCodeActionKind selectedCodeAction,
     CodeGenerationOptionsProvider fallbackOptions,
     bool allOccurrences)
 {
     _service          = service;
     _originalDocument = originalDocument;
     _generator        = SyntaxGenerator.GetGenerator(originalDocument);
     _syntaxFacts      = originalDocument.GetRequiredLanguageService <ISyntaxFactsService>();
     _semanticFacts    = originalDocument.GetRequiredLanguageService <ISemanticFactsService>();
     _expression       = expression;
     _methodSymbol     = methodSymbol;
     _containerMethod  = containingMethod;
     _actionKind       = selectedCodeAction;
     _allOccurrences   = allOccurrences;
     _fallbackOptions  = fallbackOptions;
 }
        /// <summary>
        /// Introduces a new parameter and refactors all the call sites based on the selected code action.
        /// </summary>
        private async Task <Solution> IntroduceParameterAsync(Document originalDocument, TExpressionSyntax expression,
                                                              IMethodSymbol methodSymbol, SyntaxNode containingMethod, bool allOccurrences, IntroduceParameterCodeActionKind selectedCodeAction,
                                                              CodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken)
        {
            var methodCallSites = await FindCallSitesAsync(originalDocument, methodSymbol, cancellationToken).ConfigureAwait(false);

            var modifiedSolution = originalDocument.Project.Solution;
            var rewriter         = new IntroduceParameterDocumentRewriter(this, originalDocument,
                                                                          expression, methodSymbol, containingMethod, selectedCodeAction, fallbackOptions, allOccurrences);

            foreach (var(project, projectCallSites) in methodCallSites.GroupBy(kvp => kvp.Key.Project))
            {
                var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);

                foreach (var(document, invocations) in projectCallSites)
                {
                    var newRoot = await rewriter.RewriteDocumentAsync(compilation, document, invocations, cancellationToken).ConfigureAwait(false);

                    modifiedSolution = modifiedSolution.WithDocumentSyntaxRoot(document.Id, newRoot);
                }
            }

            return(modifiedSolution);
        }
        public static async Task MakeLocalFunctionStaticAsync(
            Document document,
            LocalFunctionStatementSyntax localFunction,
            ImmutableArray <ISymbol> captures,
            SyntaxEditor syntaxEditor,
            CodeGenerationOptionsProvider fallbackOptions,
            CancellationToken cancellationToken)
        {
            var root                = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)) !;
            var semanticModel       = (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false)) !;
            var localFunctionSymbol = semanticModel.GetDeclaredSymbol(localFunction, cancellationToken);

            Contract.ThrowIfNull(localFunctionSymbol, "We should have gotten a method symbol for a local function.");
            var documentImmutableSet = ImmutableHashSet.Create(document);

            // Finds all the call sites of the local function
            var referencedSymbols = await SymbolFinder.FindReferencesAsync(
                localFunctionSymbol, document.Project.Solution, documentImmutableSet, cancellationToken).ConfigureAwait(false);

            // Now we need to find all the references to the local function that we might need to fix.
            var shouldWarn = false;

            using var builderDisposer = ArrayBuilder <InvocationExpressionSyntax> .GetInstance(out var invocations);

            foreach (var referencedSymbol in referencedSymbols)
            {
                foreach (var location in referencedSymbol.Locations)
                {
                    // We limited the search scope to the single document,
                    // so all reference should be in the same tree.
                    var referenceNode = root.FindNode(location.Location.SourceSpan);
                    if (referenceNode is not IdentifierNameSyntax identifierNode)
                    {
                        // Unexpected scenario, skip and warn.
                        shouldWarn = true;
                        continue;
                    }

                    if (identifierNode.Parent is InvocationExpressionSyntax invocation)
                    {
                        invocations.Add(invocation);
                    }
                    else
                    {
                        // We won't be able to fix non-invocation references,
                        // e.g. creating a delegate.
                        shouldWarn = true;
                    }
                }
            }

            var parameterAndCapturedSymbols = CreateParameterSymbols(captures);

            // Fix all invocations by passing in additional arguments.
            foreach (var invocation in invocations)
            {
                syntaxEditor.ReplaceNode(
                    invocation,
                    (node, generator) =>
                {
                    var currentInvocation        = (InvocationExpressionSyntax)node;
                    var seenNamedArgument        = currentInvocation.ArgumentList.Arguments.Any(a => a.NameColon != null);
                    var seenDefaultArgumentValue = currentInvocation.ArgumentList.Arguments.Count < localFunction.ParameterList.Parameters.Count;

                    var newArguments = parameterAndCapturedSymbols.Select(
                        p => (ArgumentSyntax)generator.Argument(
                            seenNamedArgument || seenDefaultArgumentValue ? p.symbol.Name : null,
                            p.symbol.RefKind,
                            p.capture.Name.ToIdentifierName()));

                    var newArgList = currentInvocation.ArgumentList.WithArguments(currentInvocation.ArgumentList.Arguments.AddRange(newArguments));
                    return(currentInvocation.WithArgumentList(newArgList));
                });
            }

            // In case any of the captured variable isn't camel-cased,
            // we need to change the referenced name inside local function to use the new parameter's name.
            foreach (var(parameter, capture) in parameterAndCapturedSymbols)
            {
                if (parameter.Name == capture.Name)
                {
                    continue;
                }

                var referencedCaptureSymbols = await SymbolFinder.FindReferencesAsync(
                    capture, document.Project.Solution, documentImmutableSet, cancellationToken).ConfigureAwait(false);

                foreach (var referencedSymbol in referencedCaptureSymbols)
                {
                    foreach (var location in referencedSymbol.Locations)
                    {
                        var referenceSpan = location.Location.SourceSpan;
                        if (!localFunction.FullSpan.Contains(referenceSpan))
                        {
                            continue;
                        }

                        var referenceNode = root.FindNode(referenceSpan);
                        if (referenceNode is IdentifierNameSyntax identifierNode)
                        {
                            syntaxEditor.ReplaceNode(
                                identifierNode,
                                (node, generator) => generator.IdentifierName(parameter.Name.ToIdentifierToken()).WithTriviaFrom(node));
                        }
                    }
                }
            }

            var codeGenerator = document.GetRequiredLanguageService <ICodeGenerationService>();
            var options       = await document.GetCodeGenerationOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false);

            var info = options.GetInfo(CodeGenerationContext.Default, document.Project);

            // Updates the local function declaration with variables passed in as parameters
            syntaxEditor.ReplaceNode(
                localFunction,
                (node, generator) =>
            {
                var localFunctionWithNewParameters = codeGenerator.AddParameters(
                    node,
                    parameterAndCapturedSymbols.SelectAsArray(p => p.symbol),
                    info,
                    cancellationToken);

                if (shouldWarn)
                {
                    var annotation = WarningAnnotation.Create(CSharpCodeFixesResources.Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code);
                    localFunctionWithNewParameters = localFunctionWithNewParameters.WithAdditionalAnnotations(annotation);
                }

                return(AddStaticModifier(localFunctionWithNewParameters, CSharpSyntaxGenerator.Instance));
            });
        }
Beispiel #16
0
 public abstract Task <ImmutableArray <SyntaxNode> > GetReplacementMembersAsync(
     Document document, IPropertySymbol property, SyntaxNode propertyDeclaration, IFieldSymbol propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken);
        public static async Task <(Document containingDocument, SyntaxAnnotation typeAnnotation)> AddTypeToExistingFileAsync(Document document, INamedTypeSymbol newType, AnnotatedSymbolMapping symbolMapping, CodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken)
        {
            var originalRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var typeDeclaration = originalRoot.GetAnnotatedNodes(symbolMapping.TypeNodeAnnotation).Single();
            var editor          = new SyntaxEditor(originalRoot, symbolMapping.AnnotatedSolution.Workspace.Services);

            var context = new CodeGenerationContext(generateMethodBodies: true);
            var options = await document.GetCodeGenerationOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false);

            var info = options.GetInfo(context, document.Project);

            var codeGenService = document.GetRequiredLanguageService <ICodeGenerationService>();
            var newTypeNode    = codeGenService.CreateNamedTypeDeclaration(newType, CodeGenerationDestination.Unspecified, info, cancellationToken)
                                 .WithAdditionalAnnotations(SimplificationHelpers.SimplifyModuleNameAnnotation);

            var typeAnnotation = new SyntaxAnnotation();

            newTypeNode = newTypeNode.WithAdditionalAnnotations(typeAnnotation);

            editor.InsertBefore(typeDeclaration, newTypeNode);

            var newDocument = document.WithSyntaxRoot(editor.GetChangedRoot());

            return(newDocument, typeAnnotation);
        }
        /// <summary>
        /// Creates new code actions for each introduce parameter possibility.
        /// Does not create actions for overloads/trampoline if there are optional parameters or if the methodSymbol
        /// is a constructor.
        /// </summary>
        private async Task <(ImmutableArray <CodeAction> actions, ImmutableArray <CodeAction> actionsAllOccurrences)?> GetActionsAsync(Document document,
                                                                                                                                       TExpressionSyntax expression, IMethodSymbol methodSymbol, SyntaxNode containingMethod, CodeGenerationOptionsProvider fallbackOptions,
                                                                                                                                       CancellationToken cancellationToken)
        {
            var(shouldDisplay, containsClassExpression) = await ShouldExpressionDisplayCodeActionAsync(
                document, expression, cancellationToken).ConfigureAwait(false);

            if (!shouldDisplay)
            {
                return(null);
            }

            using var actionsBuilder = TemporaryArray <CodeAction> .Empty;
            using var actionsBuilderAllOccurrences = TemporaryArray <CodeAction> .Empty;
            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();

            if (!containsClassExpression)
            {
                actionsBuilder.Add(CreateNewCodeAction(FeaturesResources.and_update_call_sites_directly, allOccurrences: false, IntroduceParameterCodeActionKind.Refactor));
                actionsBuilderAllOccurrences.Add(CreateNewCodeAction(FeaturesResources.and_update_call_sites_directly, allOccurrences: true, IntroduceParameterCodeActionKind.Refactor));
            }

            if (methodSymbol.MethodKind is not MethodKind.Constructor)
            {
                actionsBuilder.Add(CreateNewCodeAction(
                                       FeaturesResources.into_extracted_method_to_invoke_at_call_sites, allOccurrences: false, IntroduceParameterCodeActionKind.Trampoline));
                actionsBuilderAllOccurrences.Add(CreateNewCodeAction(
                                                     FeaturesResources.into_extracted_method_to_invoke_at_call_sites, allOccurrences: true, IntroduceParameterCodeActionKind.Trampoline));

                if (methodSymbol.MethodKind is not MethodKind.LocalFunction)
                {
                    actionsBuilder.Add(CreateNewCodeAction(
                                           FeaturesResources.into_new_overload, allOccurrences: false, IntroduceParameterCodeActionKind.Overload));
                    actionsBuilderAllOccurrences.Add(CreateNewCodeAction(
                                                         FeaturesResources.into_new_overload, allOccurrences: true, IntroduceParameterCodeActionKind.Overload));
                }
            }

            return(actionsBuilder.ToImmutableAndClear(), actionsBuilderAllOccurrences.ToImmutableAndClear());

            // Local function to create a code action with more ease
            CodeAction CreateNewCodeAction(string actionName, bool allOccurrences, IntroduceParameterCodeActionKind selectedCodeAction)
            {
                return(CodeAction.Create(
                           actionName,
                           c => IntroduceParameterAsync(document, expression, methodSymbol, containingMethod, allOccurrences, selectedCodeAction, fallbackOptions, c),
                           actionName));
            }
        }