예제 #1
0
            private ExpressionSyntax SimplifyInvocation(InvocationExpressionSyntax invocation)
            {
                var expression   = invocation.Expression;
                var memberAccess = expression as MemberAccessExpressionSyntax;

                if (memberAccess != null)
                {
                    var symbolMap      = SemanticMap.From(_document.SemanticModel, memberAccess.Expression, _cancellationToken);
                    var anySideEffects = symbolMap.AllReferencedSymbols.Any(s =>
                                                                            s.Kind == SymbolKind.Method || s.Kind == SymbolKind.Property);

                    if (anySideEffects)
                    {
                        var annotation = WarningAnnotation.Create("Warning: Expression may have side effects. Code meaning may change.");
                        expression = expression.ReplaceNode(memberAccess.Expression, memberAccess.Expression.WithAdditionalAnnotations(annotation));
                    }
                }

                return(expression.Parenthesize()
                       .WithAdditionalAnnotations(Formatter.Annotation));
            }
예제 #2
0
            public override SyntaxNode VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
            {
                node = (MemberAccessExpressionSyntax)(base.VisitMemberAccessExpression(node) ?? throw ExceptionUtilities.Unreachable);

                if (_extensionMethods.Contains(node.Name.Identifier.Text))
                {
                    // If an extension method is used as a delegate rather than invoked directly,
                    // there is no semantically valid transformation that will fully qualify the extension method.
                    // For example `Func<int> f = x.M;` is not the same as Func<int> f = () => Extensions.M(x);`
                    // since one captures x by value, and the other by reference.
                    //
                    // We will not visit this node if the parent node was an InvocationExpression,
                    // since we would have expanded the parent node entirely, rather than visiting it.
                    // Therefore it's possible that this is an extension method being used as a delegate so we warn.
                    node = node.WithAdditionalAnnotations(WarningAnnotation.Create(string.Format(
                                                                                       WorkspacesResources.Warning_adding_imports_will_bring_an_extension_method_into_scope_with_the_same_name_as_member_access,
                                                                                       node.Name.Identifier.Text)));
                }

                return(node);
            }
        protected override async Task FixAllAsync(
            Document document, ImmutableArray <Diagnostic> diagnostics,
            SyntaxEditor editor, CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var expressionTypeOpt = semanticModel.Compilation.GetTypeByMetadataName("System.Linq.Expressions.Expression`1");

            var syntaxFacts   = document.GetLanguageService <ISyntaxFactsService>();
            var semanticFacts = document.GetLanguageService <ISemanticFactsService>();
            var generator     = editor.Generator;
            var root          = editor.OriginalRoot;

            foreach (var diagnostic in diagnostics)
            {
                var conditionalExpression = root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
                var conditionExpression   = root.FindNode(diagnostic.AdditionalLocations[1].SourceSpan);
                var whenPart = root.FindNode(diagnostic.AdditionalLocations[2].SourceSpan);
                syntaxFacts.GetPartsOfConditionalExpression(
                    conditionalExpression, out var condition, out var whenTrue, out var whenFalse);

                editor.ReplaceNode(conditionalExpression,
                                   (c, g) =>
                {
                    syntaxFacts.GetPartsOfConditionalExpression(
                        c, out var currentCondition, out var currentWhenTrue, out var currentWhenFalse);

                    var coalesceExpression = whenPart == whenTrue
                            ? g.CoalesceExpression(conditionExpression, syntaxFacts.WalkDownParentheses(currentWhenTrue))
                            : g.CoalesceExpression(conditionExpression, syntaxFacts.WalkDownParentheses(currentWhenFalse));

                    if (semanticFacts.IsInExpressionTree(
                            semanticModel, conditionalExpression, expressionTypeOpt, cancellationToken))
                    {
                        coalesceExpression = coalesceExpression.WithAdditionalAnnotations(
                            WarningAnnotation.Create(FeaturesResources.Changes_to_expression_trees_may_result_in_behavior_changes_at_runtime));
                    }

                    return(coalesceExpression);
                });
예제 #4
0
        public async Task <Document> MoveDeclarationNearReferenceAsync(
            Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken)
        {
            var state = await ComputeStateAsync(document, localDeclarationStatement, cancellationToken);

            if (state == null)
            {
                return(document);
            }

            var root = await document.GetSyntaxRootAsync(cancellationToken);

            var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);

            var crossesMeaningfulBlock = CrossesMeaningfulBlock(state);
            var warningAnnotation      = crossesMeaningfulBlock
                ? WarningAnnotation.Create(FeaturesResources.Warning_colon_Declaration_changes_scope_and_may_change_meaning)
                : null;

            editor.RemoveNode(state.DeclarationStatement);

            var canMergeDeclarationAndAssignment = await CanMergeDeclarationAndAssignmentAsync(document, state, cancellationToken).ConfigureAwait(false);

            if (canMergeDeclarationAndAssignment)
            {
                MergeDeclarationAndAssignment(
                    document, state, editor, warningAnnotation);
            }
            else
            {
                await MoveDeclarationToFirstReferenceAsync(
                    document, state, editor, warningAnnotation, cancellationToken).ConfigureAwait(false);
            }

            var newRoot = editor.GetChangedRoot();

            return(document.WithSyntaxRoot(newRoot));
        }
예제 #5
0
        private static async Task <Document> GetTransformedDocumentAsync(
            Document document,
            CompilationUnitSyntax compilationUnit,
            IEnumerable <UsingDirectiveSyntax> allUsingDirectives,
            AddImportPlacement placement,
            SimplifierOptions simplifierOptions,
            CancellationToken cancellationToken)
        {
            var bannerService = document.GetRequiredLanguageService <IFileBannerFactsService>();

            // Expand usings so that they can be properly simplified after they are relocated.
            var compilationUnitWithExpandedUsings = await ExpandUsingDirectivesAsync(document, compilationUnit, allUsingDirectives, cancellationToken).ConfigureAwait(false);

            // Remove the file header from the compilation unit so that we do not lose it when making changes to usings.
            var(compilationUnitWithoutHeader, fileHeader) = RemoveFileHeader(compilationUnitWithExpandedUsings, bannerService);

            // A blanket warning that this codefix may change code so that it does not compile.
            var warningAnnotation = WarningAnnotation.Create(CSharpAnalyzersResources.Warning_colon_Moving_using_directives_may_change_code_meaning);

            var newCompilationUnit = placement == AddImportPlacement.InsideNamespace
                ? MoveUsingsInsideNamespace(compilationUnitWithoutHeader, warningAnnotation)
                : MoveUsingsOutsideNamespaces(compilationUnitWithoutHeader, warningAnnotation);

            // Re-attach the header now that using have been moved and LeadingTrivia is no longer being altered.
            var newCompilationUnitWithHeader = AddFileHeader(newCompilationUnit, fileHeader);
            var newDocument = document.WithSyntaxRoot(newCompilationUnitWithHeader);

            // Simplify usings now that they have been moved and are in the proper context.
#if CODE_STYLE
#pragma warning disable RS0030 // Do not used banned APIs (ReduceAsync with SimplifierOptions isn't public)
            return(await Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, optionSet : null, cancellationToken).ConfigureAwait(false));

#pragma warning restore
#else
            return(await Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, simplifierOptions, cancellationToken).ConfigureAwait(false));
#endif
        }
예제 #6
0
 protected SyntaxAnnotation CreateWarningAnnotation()
 => WarningAnnotation.Create(FeaturesResources.Warning_colon_semantics_may_change_when_converting_statement);
예제 #7
0
        private static void ApplyEdit(
            SyntaxEditor editor,
            SemanticModel semanticModel,
            INamedTypeSymbol expressionTypeOpt,
            ISyntaxFactsService syntaxFacts,
            ISemanticFactsService semanticFacts,
            Diagnostic diagnostic,
            CancellationToken cancellationToken
            )
        {
            var root = editor.OriginalRoot;
            var conditionalExpression = root.FindNode(
                diagnostic.AdditionalLocations[0].SourceSpan,
                getInnermostNodeForTie: true
                );
            var conditionalPartHigh = root.FindNode(diagnostic.AdditionalLocations[1].SourceSpan);
            var whenPart            = root.FindNode(diagnostic.AdditionalLocations[2].SourceSpan);

            syntaxFacts.GetPartsOfConditionalExpression(
                conditionalExpression,
                out var condition,
                out var whenTrue,
                out var whenFalse
                );

            var conditionalPartLow = syntaxFacts.WalkDownParentheses(conditionalPartHigh);

            editor.ReplaceNode(
                conditionalExpression,
                (c, g) =>
            {
                syntaxFacts.GetPartsOfConditionalExpression(
                    c,
                    out var currentCondition,
                    out var currentWhenTrue,
                    out var currentWhenFalse
                    );

                var coalesceExpression = GetCoalesceExpression(
                    syntaxFacts,
                    g,
                    whenPart,
                    whenTrue,
                    conditionalPartLow,
                    currentWhenTrue,
                    currentWhenFalse
                    )
                                         .WithTrailingTrivia(conditionalExpression.GetTrailingTrivia());

                if (
                    semanticFacts.IsInExpressionTree(
                        semanticModel,
                        conditionalExpression,
                        expressionTypeOpt,
                        cancellationToken
                        )
                    )
                {
                    coalesceExpression = coalesceExpression.WithAdditionalAnnotations(
                        WarningAnnotation.Create(
                            AnalyzersResources.Changes_to_expression_trees_may_result_in_behavior_changes_at_runtime
                            )
                        );
                }

                return(coalesceExpression.WithAdditionalAnnotations(Formatter.Annotation));
            }
        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));
            });
        }
예제 #9
0
        private async Task <Document> ConvertToGeneratedDllImport(
            Document doc,
            MethodDeclarationSyntax methodSyntax,
            IMethodSymbol methodSymbol,
            AttributeData dllImportAttr,
            INamedTypeSymbol generatedDllImportAttrType,
            bool usePreprocessorDefines,
            CancellationToken cancellationToken)
        {
            DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false);

            SyntaxGenerator generator = editor.Generator;

            var dllImportSyntax = (AttributeSyntax)dllImportAttr !.ApplicationSyntaxReference !.GetSyntax(cancellationToken);

            // Create GeneratedDllImport attribute based on the DllImport attribute
            SyntaxNode generatedDllImportSyntax = GetGeneratedDllImportAttribute(
                editor,
                generator,
                dllImportSyntax,
                methodSymbol.GetDllImportData() !,
                generatedDllImportAttrType,
                out SyntaxNode? unmanagedCallConvAttributeMaybe);

            // Add annotation about potential behavioural and compatibility changes
            generatedDllImportSyntax = generatedDllImportSyntax.WithAdditionalAnnotations(
                WarningAnnotation.Create(string.Format(Resources.ConvertToGeneratedDllImportWarning, "[TODO] Documentation link")));

            // Replace DllImport with GeneratedDllImport
            SyntaxNode generatedDeclaration = generator.ReplaceNode(methodSyntax, dllImportSyntax, generatedDllImportSyntax);

            if (unmanagedCallConvAttributeMaybe is not null)
            {
                generatedDeclaration = generator.AddAttributes(generatedDeclaration, unmanagedCallConvAttributeMaybe);
            }

            // Replace extern keyword with partial keyword
            generatedDeclaration = generator.WithModifiers(
                generatedDeclaration,
                generator.GetModifiers(methodSyntax)
                .WithIsExtern(false)
                .WithPartial(true));

            if (!usePreprocessorDefines)
            {
                // Replace the original method with the updated one
                editor.ReplaceNode(methodSyntax, generatedDeclaration);
            }
            else
            {
                // #if DLLIMPORTGENERATOR_ENABLED
                generatedDeclaration = generatedDeclaration.WithLeadingTrivia(
                    generatedDeclaration.GetLeadingTrivia()
                    .AddRange(new[] {
                    SyntaxFactory.Trivia(SyntaxFactory.IfDirectiveTrivia(SyntaxFactory.IdentifierName("DLLIMPORTGENERATOR_ENABLED"), isActive: true, branchTaken: true, conditionValue: true)),
                    SyntaxFactory.ElasticMarker
                }));

                // #else
                generatedDeclaration = generatedDeclaration.WithTrailingTrivia(
                    generatedDeclaration.GetTrailingTrivia()
                    .AddRange(new[] {
                    SyntaxFactory.Trivia(SyntaxFactory.ElseDirectiveTrivia(isActive: false, branchTaken: false)),
                    SyntaxFactory.ElasticMarker
                }));

                // Remove existing leading trivia - it will be on the GeneratedDllImport method
                MethodDeclarationSyntax updatedDeclaration = methodSyntax.WithLeadingTrivia();

                // #endif
                updatedDeclaration = updatedDeclaration.WithTrailingTrivia(
                    methodSyntax.GetTrailingTrivia()
                    .AddRange(new[] {
                    SyntaxFactory.Trivia(SyntaxFactory.EndIfDirectiveTrivia(isActive: true)),
                    SyntaxFactory.ElasticMarker
                }));

                // Add the GeneratedDllImport method
                editor.InsertBefore(methodSyntax, generatedDeclaration);

                // Replace the original method with the updated DllImport method
                editor.ReplaceNode(methodSyntax, updatedDeclaration);
            }

            return(editor.GetChangedDocument());
        }
        private static async Task <Document> InlineTemporaryAsync(Document document, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken)
        {
            var workspace = document.Project.Solution.Workspace;

            // Annotate the variable declarator so that we can get back to it later.
            var updatedDocument = await document.ReplaceNodeAsync(declarator, declarator.WithAdditionalAnnotations(DefinitionAnnotation), cancellationToken).ConfigureAwait(false);

            var semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);

            // Create the expression that we're actually going to inline.
            var expressionToInline = await CreateExpressionToInlineAsync(variableDeclarator, updatedDocument, cancellationToken).ConfigureAwait(false);

            // Collect the identifier names for each reference.
            var local      = (ILocalSymbol)semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken);
            var symbolRefs = await SymbolFinder.FindReferencesAsync(local, updatedDocument.Project.Solution, cancellationToken).ConfigureAwait(false);

            var referencedSymbol = symbolRefs.SingleOrDefault(r => Equals(r.Definition, local));
            var references       = referencedSymbol == null?SpecializedCollections.EmptyEnumerable <ReferenceLocation>() : referencedSymbol.Locations;

            var syntaxRoot = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            // Collect the topmost parenting expression for each reference.
            var nonConflictingIdentifierNodes = references
                                                .Select(loc => (IdentifierNameSyntax)syntaxRoot.FindToken(loc.Location.SourceSpan.Start).Parent)
                                                .Where(ident => !HasConflict(ident, variableDeclarator));

            // Add referenceAnnotations to identifier nodes being replaced.
            updatedDocument = await updatedDocument.ReplaceNodesAsync(
                nonConflictingIdentifierNodes,
                (o, n) => n.WithAdditionalAnnotations(ReferenceAnnotation),
                cancellationToken).ConfigureAwait(false);

            semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);

            // Get the annotated reference nodes.
            nonConflictingIdentifierNodes = await FindReferenceAnnotatedNodesAsync(updatedDocument, cancellationToken).ConfigureAwait(false);

            var topmostParentingExpressions = nonConflictingIdentifierNodes
                                              .Select(ident => GetTopMostParentingExpression(ident))
                                              .Distinct().ToList();

            var originalInitializerSymbolInfo = semanticModel.GetSymbolInfo(variableDeclarator.Initializer.Value, cancellationToken);

            // Checks to see if inlining the temporary variable may change the code's meaning. This can only apply if the variable has two or more
            // references. We later use this heuristic to determine whether or not to display a warning message to the user.
            var mayContainSideEffects = references.Count() > 1 &&
                                        MayContainSideEffects(variableDeclarator.Initializer.Value);

            // Make each topmost parenting statement or Equals Clause Expressions semantically explicit.
            updatedDocument = await updatedDocument.ReplaceNodesAsync(topmostParentingExpressions, (o, n) =>
            {
                var node = Simplifier.Expand(n, semanticModel, workspace, cancellationToken: cancellationToken);

                // warn when inlining into a conditional expression, as the inlined expression will not be executed.
                if (semanticModel.GetSymbolInfo(o).Symbol is IMethodSymbol {
                    IsConditional : true
                })
                {
                    node = node.WithAdditionalAnnotations(
                        WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_into_conditional_method_call));
                }

                // If the refactoring may potentially change the code's semantics, display a warning message to the user.
                if (mayContainSideEffects)
                {
                    node = node.WithAdditionalAnnotations(
                        WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_variable_may_change_code_meaning));
                }

                return(node);
            }, cancellationToken).ConfigureAwait(false);
예제 #11
0
            protected override async Task <Document?> FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray <Diagnostic> diagnostics)
            {
                DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false);

                SyntaxGenerator generator = editor.Generator;

                SyntaxNode?root = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);

                if (root == null)
                {
                    return(document);
                }

                foreach (Diagnostic diagnostic in diagnostics)
                {
                    // Get the syntax node tied to the diagnostic and check that it is a method declaration
                    if (root.FindNode(diagnostic.Location.SourceSpan) is not MethodDeclarationSyntax methodSyntax)
                    {
                        continue;
                    }
                    if (editor.SemanticModel.GetDeclaredSymbol(methodSyntax, fixAllContext.CancellationToken) is not IMethodSymbol methodSymbol)
                    {
                        continue;
                    }

                    SyntaxNode generatedDeclaration = await ConvertMethodDeclarationToLibraryImport(methodSyntax, editor, generator, methodSymbol, GetSuffixFromEquivalenceKey(fixAllContext.CodeActionEquivalenceKey), fixAllContext.CancellationToken).ConfigureAwait(false);

                    if (!methodSymbol.MethodImplementationFlags.HasFlag(System.Reflection.MethodImplAttributes.PreserveSig))
                    {
                        bool shouldWarn = await TransformCallersOfNoPreserveSigMethod(editor, methodSymbol, fixAllContext.CancellationToken).ConfigureAwait(false);

                        if (shouldWarn)
                        {
                            generatedDeclaration = generatedDeclaration.WithAdditionalAnnotations(WarningAnnotation.Create(SR.ConvertNoPreserveSigDllImportToGeneratedMayProduceInvalidCode));
                        }
                    }

                    // Replace the original method with the updated one
                    editor.ReplaceNode(methodSyntax, generatedDeclaration);

                    MakeEnclosingTypesPartial(editor, methodSyntax);
                }

                return(editor.GetChangedDocument());
            }
예제 #12
0
        private async Task <Document> ConvertToLibraryImport(
            Document doc,
            MethodDeclarationSyntax methodSyntax,
            IMethodSymbol methodSymbol,
            AttributeData dllImportAttr,
            INamedTypeSymbol generatedDllImportAttrType,
            char?entryPointSuffix,
            CancellationToken cancellationToken)
        {
            DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false);

            SyntaxGenerator generator = editor.Generator;

            var dllImportSyntax = (AttributeSyntax)await dllImportAttr !.ApplicationSyntaxReference !.GetSyntaxAsync(cancellationToken).ConfigureAwait(false);

            // Create GeneratedDllImport attribute based on the DllImport attribute
            SyntaxNode generatedDllImportSyntax = GetGeneratedDllImportAttribute(
                editor,
                generator,
                dllImportSyntax,
                methodSymbol,
                generatedDllImportAttrType,
                entryPointSuffix,
                out SyntaxNode? unmanagedCallConvAttributeMaybe);

            // Add annotation about potential behavioural and compatibility changes
            generatedDllImportSyntax = generatedDllImportSyntax.WithAdditionalAnnotations(
                WarningAnnotation.Create(string.Format(Resources.ConvertToLibraryImportWarning, "[TODO] Documentation link")));

            // Replace DllImport with GeneratedDllImport
            SyntaxNode generatedDeclaration = generator.ReplaceNode(methodSyntax, dllImportSyntax, generatedDllImportSyntax);

            if (!methodSymbol.MethodImplementationFlags.HasFlag(System.Reflection.MethodImplAttributes.PreserveSig))
            {
                generatedDeclaration = await RemoveNoPreserveSigTransform(editor, generatedDeclaration, methodSymbol, cancellationToken).ConfigureAwait(false);
            }

            if (unmanagedCallConvAttributeMaybe is not null)
            {
                generatedDeclaration = generator.AddAttributes(generatedDeclaration, unmanagedCallConvAttributeMaybe);
            }

            // Replace extern keyword with partial keyword
            generatedDeclaration = generator.WithModifiers(
                generatedDeclaration,
                generator.GetModifiers(methodSyntax)
                .WithIsExtern(false)
                .WithPartial(true));

            foreach (IParameterSymbol parameter in methodSymbol.Parameters)
            {
                if (parameter.Type.SpecialType == SpecialType.System_Boolean &&
                    !parameter.GetAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_InteropServices_MarshalAsAttribute))
                {
                    MethodDeclarationSyntax generatedDeclarationSyntax = (MethodDeclarationSyntax)generatedDeclaration;
                    ParameterSyntax         generatedParameterSyntax   = generatedDeclarationSyntax.ParameterList.Parameters[parameter.Ordinal];
                    generatedDeclaration = generator.ReplaceNode(generatedDeclaration, generatedParameterSyntax, generator.AddAttributes(generatedParameterSyntax,
                                                                                                                                         GenerateMarshalAsUnmanagedTypeBoolAttribute(generator)));
                }
            }

            if (methodSymbol.ReturnType.SpecialType == SpecialType.System_Boolean &&
                !methodSymbol.GetReturnTypeAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_InteropServices_MarshalAsAttribute))
            {
                generatedDeclaration = generator.AddReturnAttributes(generatedDeclaration,
                                                                     GenerateMarshalAsUnmanagedTypeBoolAttribute(generator));
            }

            // Replace the original method with the updated one
            editor.ReplaceNode(methodSyntax, generatedDeclaration);

            return(editor.GetChangedDocument());
        }
예제 #13
0
 protected static SyntaxAnnotation CreatePossibleInvalidCodeWarning()
 {
     return(WarningAnnotation.Create(MicrosoftNetCoreAnalyzersResources.MakeMethodDeclaredOnImplementationTypeStaticMayProduceInvalidCode));
 }
예제 #14
0
        public override async Task <SelectionResult> GetValidSelectionAsync(CancellationToken cancellationToken)
        {
            if (!this.ContainsValidSelection)
            {
                return(NullSelection);
            }

            var text  = this.SemanticDocument.Text;
            var root  = this.SemanticDocument.Root;
            var model = this.SemanticDocument.SemanticModel;
            var doc   = this.SemanticDocument;

            // go through pipe line and calculate information about the user selection
            var selectionInfo = GetInitialSelectionInfo(root, text, cancellationToken);

            selectionInfo = AssignInitialFinalTokens(selectionInfo, root, cancellationToken);
            selectionInfo = AdjustFinalTokensBasedOnContext(selectionInfo, model, cancellationToken);
            selectionInfo = AssignFinalSpan(selectionInfo, text, cancellationToken);
            selectionInfo = ApplySpecialCases(selectionInfo, text, cancellationToken);
            selectionInfo = CheckErrorCasesAndAppendDescriptions(selectionInfo, root, model, cancellationToken);

            // there was a fatal error that we couldn't even do negative preview, return error result
            if (selectionInfo.Status.FailedWithNoBestEffortSuggestion())
            {
                return(new ErrorSelectionResult(selectionInfo.Status));
            }

            var controlFlowSpan = GetControlFlowSpan(selectionInfo);

            if (!selectionInfo.SelectionInExpression)
            {
                var statementRange = GetStatementRangeContainedInSpan <StatementSyntax>(root, controlFlowSpan, cancellationToken);
                if (statementRange == null)
                {
                    selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.None, CSharpFeaturesResources.Can_t_determine_valid_range_of_statements_to_extract));
                    return(new ErrorSelectionResult(selectionInfo.Status));
                }

                var isFinalSpanSemanticallyValid = IsFinalSpanSemanticallyValidSpan(model, controlFlowSpan, statementRange, cancellationToken);
                if (!isFinalSpanSemanticallyValid)
                {
                    // check control flow only if we are extracting statement level, not expression
                    // level. you can not have goto that moves control out of scope in expression level
                    // (even in lambda)
                    selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.BestEffort, CSharpFeaturesResources.Not_all_code_paths_return));
                }
            }

            // Warn if local functions are in selection since data flow analysis
            // cannot correctly analyze them
            // https://github.com/dotnet/roslyn/issues/14214
            if (SpanInvolvesLocalFunction(selectionInfo.FinalSpan, model, root))
            {
                selectionInfo = selectionInfo.WithStatus(s => s.With(
                                                             OperationStatusFlag.Succeeded | OperationStatusFlag.BestEffort,
                                                             CSharpFeaturesResources.Warning_Extracting_a_local_function_reference_may_produce_invalid_code));
                var commonRoot = selectionInfo.CommonRootFromOriginalSpan;
                var annotated  = commonRoot.WithAdditionalAnnotations(
                    WarningAnnotation.Create(CSharpFeaturesResources.Warning_Extracting_a_local_function_reference_may_produce_invalid_code));
                doc = await doc.WithSyntaxRootAsync(
                    root.ReplaceNode(commonRoot, annotated),
                    cancellationToken).ConfigureAwait(false);

                selectionInfo.FirstTokenInOriginalSpan = doc.Root.FindToken(selectionInfo.FirstTokenInOriginalSpan.SpanStart);
                selectionInfo.LastTokenInOriginalSpan  = doc.Root.FindToken(selectionInfo.LastTokenInOriginalSpan.SpanStart);
                selectionInfo.FirstTokenInFinalSpan    = doc.Root.FindToken(selectionInfo.FirstTokenInFinalSpan.SpanStart);
                selectionInfo.LastTokenInFinalSpan     = doc.Root.FindToken(selectionInfo.LastTokenInFinalSpan.SpanStart);
            }

            return(await CSharpSelectionResult.CreateAsync(
                       selectionInfo.Status,
                       selectionInfo.OriginalSpan,
                       selectionInfo.FinalSpan,
                       this.Options,
                       selectionInfo.SelectionInExpression,
                       doc,
                       selectionInfo.FirstTokenInFinalSpan,
                       selectionInfo.LastTokenInFinalSpan,
                       cancellationToken).ConfigureAwait(false));
        }
        public async Task <Document> MoveDeclarationNearReferenceAsync(
            Document document,
            SyntaxNode localDeclarationStatement,
            CancellationToken cancellationToken
            )
        {
            var state = await ComputeStateAsync(
                document,
                localDeclarationStatement,
                cancellationToken
                )
                        .ConfigureAwait(false);

            if (state == null)
            {
                return(document);
            }

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);

            var crossesMeaningfulBlock = CrossesMeaningfulBlock(state);
            var warningAnnotation      = crossesMeaningfulBlock
                ? WarningAnnotation.Create(
                WorkspaceExtensionsResources.Warning_colon_Declaration_changes_scope_and_may_change_meaning
                )
                : null;

            var canMergeDeclarationAndAssignment = await CanMergeDeclarationAndAssignmentAsync(
                document,
                state,
                cancellationToken
                )
                                                   .ConfigureAwait(false);

            if (canMergeDeclarationAndAssignment)
            {
                editor.RemoveNode(state.DeclarationStatement);
                MergeDeclarationAndAssignment(document, state, editor, warningAnnotation);
            }
            else
            {
                var statementIndex = state.OutermostBlockStatements.IndexOf(
                    state.DeclarationStatement
                    );
                if (
                    statementIndex + 1 < state.OutermostBlockStatements.Count &&
                    state.OutermostBlockStatements[statementIndex + 1]
                    == state.FirstStatementAffectedInInnermostBlock
                    )
                {
                    // Already at the correct location.
                    return(document);
                }

                editor.RemoveNode(state.DeclarationStatement);
                await MoveDeclarationToFirstReferenceAsync(
                    document,
                    state,
                    editor,
                    warningAnnotation,
                    cancellationToken
                    )
                .ConfigureAwait(false);
            }

            var newRoot = editor.GetChangedRoot();

            return(document.WithSyntaxRoot(newRoot));
        }