Exemplo n.º 1
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var document = context.Document;
            var cancellationToken = context.CancellationToken;

            var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var compilationUnit = (CompilationUnitSyntax)syntaxRoot;

#if CODE_STYLE
            var options = document.Project.AnalyzerOptions.GetAnalyzerOptionSet(syntaxRoot.SyntaxTree, cancellationToken);
            var simplifierOptions = CSharpSimplifierOptions.Create(options, fallbackOptions: null);
#else
            var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
            var simplifierOptions = await SimplifierOptions.FromDocumentAsync(document, fallbackOptions: context.Options(document.Project.LanguageServices).SimplifierOptions, cancellationToken).ConfigureAwait(false);
#endif
            var codeStyleOption = options.GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement);

            // Read the preferred placement option and verify if it can be applied to this code file.
            // There are cases where we will not be able to fix the diagnostic and the user will need to resolve
            // it manually.
            var (placement, preferPreservation) = DeterminePlacement(compilationUnit, codeStyleOption);
            if (preferPreservation)
                return;

            foreach (var diagnostic in context.Diagnostics)
            {
                context.RegisterCodeFix(
                    CodeAction.Create(
                        CSharpAnalyzersResources.Move_misplaced_using_directives,
                        token => GetTransformedDocumentAsync(document, compilationUnit, GetAllUsingDirectives(compilationUnit), placement, simplifierOptions, token),
                        nameof(CSharpAnalyzersResources.Move_misplaced_using_directives)),
                    diagnostic);
            }
        }
Exemplo n.º 2
0
        internal static async Task<Document> TransformDocumentIfRequiredAsync(
            Document document,
            SimplifierOptions simplifierOptions,
            CodeStyleOption2<AddImportPlacement> importPlacementStyleOption,
            CancellationToken cancellationToken)
        {
            var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var compilationUnit = (CompilationUnitSyntax)syntaxRoot;

            var (placement, preferPreservation) = DeterminePlacement(compilationUnit, importPlacementStyleOption);
            if (preferPreservation)
            {
                return document;
            }

            // We are called from a diagnostic, but also for all new documents, so check if there are any usings at all
            // otherwise there is nothing to do.
            var allUsingDirectives = GetAllUsingDirectives(compilationUnit);
            if (allUsingDirectives.Count == 0)
            {
                return document;
            }

            return await GetTransformedDocumentAsync(document, compilationUnit, allUsingDirectives, placement, simplifierOptions, cancellationToken).ConfigureAwait(false);
        }
        /// <summary>
        /// Checks a member access expression <c>expr.Name</c> and, if it is of the form <c>this.Name</c> or
        /// <c>Me.Name</c> determines if it is safe to replace with just <c>Name</c> alone.
        /// </summary>
        public bool ShouldSimplifyThisMemberAccessExpression(
            TMemberAccessExpressionSyntax?memberAccessExpression,
            SemanticModel semanticModel,
            SimplifierOptions simplifierOptions,
            [NotNullWhen(true)] out TThisExpressionSyntax?thisExpression,
            out ReportDiagnostic severity,
            CancellationToken cancellationToken)
        {
            severity       = default;
            thisExpression = null;

            if (memberAccessExpression is null)
            {
                return(false);
            }

            var syntaxFacts = this.SyntaxFacts;

            if (!syntaxFacts.IsSimpleMemberAccessExpression(memberAccessExpression))
            {
                return(false);
            }

            thisExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccessExpression) as TThisExpressionSyntax;
            if (!syntaxFacts.IsThisExpression(thisExpression))
            {
                return(false);
            }

            var symbolInfo = semanticModel.GetSymbolInfo(memberAccessExpression, cancellationToken);

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

            if (!simplifierOptions.TryGetQualifyMemberAccessOption(symbolInfo.Symbol.Kind, out var optionValue))
            {
                return(false);
            }

            // We always simplify a static accesses off of this/me.  Otherwise, we fall back to whatever the user's option is.
            if (!symbolInfo.Symbol.IsStatic && optionValue.Value)
            {
                return(false);
            }

            var speculationAnalyzer = GetSpeculationAnalyzer(semanticModel, memberAccessExpression, cancellationToken);
            var newSymbolInfo       = speculationAnalyzer.SpeculativeSemanticModel.GetSymbolInfo(speculationAnalyzer.ReplacedExpression, cancellationToken);

            if (!symbolInfo.Symbol.Equals(newSymbolInfo.Symbol, SymbolEqualityComparer.IncludeNullability))
            {
                return(false);
            }

            severity = optionValue.Notification.Severity;
            return(!semanticModel.SyntaxTree.OverlapsHiddenPosition(memberAccessExpression.Span, cancellationToken) &&
                   !MayCauseParseDifference(memberAccessExpression));
        }
Exemplo n.º 4
0
    private async Task <string?> GetSimplifiedEnumNameAsync(
        Document document,
        string fullyQualifiedTypeName,
        string firstEnumMemberName,
        TextSpan caseGenerationLocation,
        SimplifierOptions simplifierOptions,
        CancellationToken cancellationToken)
    {
        // Insert switch with enum case into the document.
        var(documentWithFullyQualified, fullyQualifiedTypeLocation) = await GetDocumentWithEnumCaseAsync(document, fullyQualifiedTypeName, firstEnumMemberName, caseGenerationLocation, cancellationToken).ConfigureAwait(false);

        // Simplify enum case.
        var simplifiedEnum = await GetSimplifiedTypeNameAtSpanAsync(documentWithFullyQualified, fullyQualifiedTypeLocation, simplifierOptions, cancellationToken).ConfigureAwait(false);

        return(simplifiedEnum);
    }
Exemplo n.º 5
0
        public void OptionsAreMessagePackSerializable(string language)
        {
            var messagePackOptions = MessagePackSerializerOptions.Standard.WithResolver(MessagePackFormatters.DefaultResolver);

            using var workspace = new AdhocWorkspace();
            var languageServices = workspace.Services.GetLanguageServices(language);

            var options = new object[]
            {
                SimplifierOptions.GetDefault(languageServices),
                SyntaxFormattingOptions.GetDefault(languageServices),
                CodeCleanupOptions.GetDefault(languageServices),
                CodeGenerationOptions.GetDefault(languageServices),
                IdeCodeStyleOptions.GetDefault(languageServices),
                CodeActionOptions.GetDefault(languageServices),
                IndentationOptions.GetDefault(languageServices),
                ExtractMethodGenerationOptions.GetDefault(languageServices),

                // some non-default values:
                new VisualBasicIdeCodeStyleOptions(
                    new IdeCodeStyleOptions.CommonOptions()
                {
                    AllowStatementImmediatelyAfterBlock = new CodeStyleOption2 <bool>(false, NotificationOption2.Error)
                },
                    PreferredModifierOrder: new CodeStyleOption2 <string>("Public Private", NotificationOption2.Error))
            };

            foreach (var original in options)
            {
                using var stream = new MemoryStream();
                MessagePackSerializer.Serialize(stream, original, messagePackOptions);
                stream.Position = 0;

                var deserialized = MessagePackSerializer.Deserialize(original.GetType(), stream, messagePackOptions);
                Assert.Equal(original, deserialized);
            }
        }
Exemplo n.º 6
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
        }
 public bool OpenFileOnly(SimplifierOptions options)
 {
     return(true);
 }
Exemplo n.º 8
0
 ValueTask <SimplifierOptions> OptionsProvider <SimplifierOptions> .GetOptionsAsync(HostLanguageServices languageServices, CancellationToken cancellationToken)
 => ValueTaskFactory.FromResult(GetOptions(languageServices).CleanupOptions?.SimplifierOptions ?? SimplifierOptions.GetDefault(languageServices));
Exemplo n.º 9
0
    private static async Task <string?> GetSimplifiedTypeNameAtSpanAsync(Document documentWithFullyQualifiedTypeName, TextSpan fullyQualifiedTypeSpan, SimplifierOptions simplifierOptions, CancellationToken cancellationToken)
    {
        // Simplify
        var typeAnnotation = new SyntaxAnnotation();
        var syntaxRoot     = await documentWithFullyQualifiedTypeName.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

        var nodeToReplace = syntaxRoot.DescendantNodes().FirstOrDefault(n => n.Span == fullyQualifiedTypeSpan);

        if (nodeToReplace == null)
        {
            return(null);
        }

        var updatedRoot             = syntaxRoot.ReplaceNode(nodeToReplace, nodeToReplace.WithAdditionalAnnotations(typeAnnotation, Simplifier.Annotation));
        var documentWithAnnotations = documentWithFullyQualifiedTypeName.WithSyntaxRoot(updatedRoot);

        var simplifiedDocument = await Simplifier.ReduceAsync(documentWithAnnotations, simplifierOptions, cancellationToken).ConfigureAwait(false);

        var simplifiedRoot = await simplifiedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

        var simplifiedTypeName = simplifiedRoot.GetAnnotatedNodesAndTokens(typeAnnotation).Single().ToString();

        return(simplifiedTypeName);
    }
Exemplo n.º 10
0
 public bool OpenFileOnly(SimplifierOptions options) => false;
Exemplo n.º 11
0
 public sealed override bool IsApplicable(SimplifierOptions options)
 => IsApplicable((CSharpSimplifierOptions)options);
Exemplo n.º 12
0
            private static SyntaxNode SimplifyAnonymousTypeMemberName(AnonymousObjectMemberDeclaratorSyntax node, SemanticModel semanticModel, SimplifierOptions options, CancellationToken canellationToken)
            {
                if (CanSimplifyAnonymousTypeMemberName(node))
                {
                    return(node.WithNameEquals(null).WithTriviaFrom(node));
                }

                return(node);
            }
Exemplo n.º 13
0
            private ArgumentSyntax SimplifyTupleName(ArgumentSyntax node, SemanticModel semanticModel, SimplifierOptions options, CancellationToken cancellationToken)
            {
                if (CanSimplifyTupleElementName(node, this.ParseOptions))
                {
                    return(node.WithNameColon(null).WithTriviaFrom(node));
                }

                return(node);
            }
Exemplo n.º 14
0
    /// <summary>
    /// For a document with the default switch snippet inserted, generate the expanded set of cases based on the value
    /// of the field currently inserted into the switch statement.
    /// </summary>
    public async Task <string?> GetSwitchExpansionAsync(Document document, TextSpan caseGenerationLocation, TextSpan switchExpressionLocation, SimplifierOptions simplifierOptions, CancellationToken cancellationToken)
    {
        var typeSymbol = await GetEnumSymbolAsync(document, switchExpressionLocation, cancellationToken).ConfigureAwait(false);

        if (typeSymbol?.TypeKind != TypeKind.Enum)
        {
            return(null);
        }

        var enumFields = typeSymbol.GetMembers().Where(m => m.Kind == SymbolKind.Field && m.IsStatic);

        if (!enumFields.Any())
        {
            return(null);
        }

        // Find and use the most simplified legal version of the enum type name in this context
        var fullyQualifiedEnumName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
        var simplifiedTypeName     = await GetSimplifiedEnumNameAsync(document, fullyQualifiedEnumName, enumFields.First().Name, caseGenerationLocation, simplifierOptions, cancellationToken).ConfigureAwait(false);

        if (simplifiedTypeName == null)
        {
            return(null);
        }

        using var _ = PooledStringBuilder.GetInstance(out var casesBuilder);
        foreach (var member in enumFields)
        {
            casesBuilder.AppendFormat(SwitchCaseFormat, simplifiedTypeName, member.Name);
        }

        casesBuilder.Append(SwitchDefaultCaseForm);
        return(casesBuilder.ToString());
    }
Exemplo n.º 15
0
    /// <summary>
    /// For a specified snippet field, replace it with the fully qualified name then simplify in the context of the document
    /// in order to retrieve the simplified type name.
    /// </summary>
    public static async Task <string?> GetSimplifiedTypeNameAsync(Document document, TextSpan fieldSpan, string fullyQualifiedTypeName, SimplifierOptions simplifierOptions, CancellationToken cancellationToken)
    {
        // Insert the function parameter (fully qualified type name) into the document.
        var updatedTextSpan = new TextSpan(fieldSpan.Start, fullyQualifiedTypeName.Length);

        var textChange = new TextChange(fieldSpan, fullyQualifiedTypeName);
        var text       = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

        var documentWithFullyQualifiedTypeName = document.WithText(text.WithChanges(textChange));

        // Simplify
        var simplifiedTypeName = await GetSimplifiedTypeNameAtSpanAsync(documentWithFullyQualifiedTypeName, updatedTextSpan, simplifierOptions, cancellationToken).ConfigureAwait(false);

        return(simplifiedTypeName);
    }
Exemplo n.º 16
0
    /// <summary>
    /// Formats the snippet by applying the snippet to the document with the default values / function results for snippet declarations.
    /// Then converts back into an LSP snippet by replacing the declarations with the appropriate LSP tab stops.
    ///
    /// Note that the operations in this method are sensitive to the context in the document and so must be calculated on each request.
    /// </summary>
    private static async Task <string> GetFormattedLspSnippetAsync(
        ParsedXmlSnippet parsedSnippet,
        TextSpan snippetShortcut,
        Document originalDocument,
        SourceText originalSourceText,
        SyntaxFormattingOptions formattingOptions,
        SimplifierOptions simplifierOptions,
        CancellationToken cancellationToken)
    {
        // Calculate the snippet text with defaults + snippet function results.
        var(snippetFullText, fields, caretSpan) = await GetReplacedSnippetTextAsync(
            originalDocument, originalSourceText, snippetShortcut, parsedSnippet, simplifierOptions, cancellationToken).ConfigureAwait(false);

        // Create a document with the default snippet text that we can use to format the snippet.
        var textChange         = new TextChange(snippetShortcut, snippetFullText);
        var snippetEndPosition = textChange.Span.Start + textChange.NewText !.Length;

        var documentWithSnippetText = originalSourceText.WithChanges(textChange);
        var root = await originalDocument.WithText(documentWithSnippetText).GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

        var spanToFormat      = TextSpan.FromBounds(textChange.Span.Start, snippetEndPosition);
        var formattingChanges = Formatter.GetFormattedTextChanges(root, spanToFormat, originalDocument.Project.Solution.Services, formattingOptions, cancellationToken: cancellationToken)
                                ?.ToImmutableArray() ?? ImmutableArray <TextChange> .Empty;

        var formattedText = documentWithSnippetText.WithChanges(formattingChanges);

        // We now have a formatted snippet with default values.  We need to
        // replace the fields and caret with the proper LSP tab stop notation.
        // Since formatting changes are entirely whitespace, we can calculate the new locations by
        // adjusting the old spans based on the formatting changes that occured before them.

        // Get the adjusted snippet bounds.
        snippetEndPosition = GetAdjustedSpan(formattingChanges, new TextSpan(snippetEndPosition, 0)).Start;
        var spanContainingFormattedSnippet = TextSpan.FromBounds(snippetShortcut.Start, snippetEndPosition);

        // Get the adjusted fields and determine the text edits to make LSP formatted tab stops.
        using var _1 = ArrayBuilder <TextChange> .GetInstance(out var lspTextChanges);

        foreach (var(field, spans) in fields)
        {
            var lspTextForField = string.IsNullOrEmpty(field.DefaultText) ? $"${{{field.EditIndex}}}" : $"${{{field.EditIndex}:{field.DefaultText}}}";
            foreach (var span in spans)
            {
                // Adjust the span based on the formatting changes and build the snippet text change.
                var fieldInFormattedText  = GetAdjustedSpan(formattingChanges, span);
                var fieldInSnippetContext = GetTextSpanInContextOfSnippet(fieldInFormattedText.Start, spanContainingFormattedSnippet.Start, fieldInFormattedText.Length);
                lspTextChanges.Add(new TextChange(fieldInSnippetContext, lspTextForField));
            }
        }

        // Get the adjusted caret location and replace the placeholder comment with the LSP formatted tab stop.
        if (caretSpan != null)
        {
            var caretInFormattedText  = GetAdjustedSpan(formattingChanges, caretSpan.Value);
            var caretInSnippetContext = GetTextSpanInContextOfSnippet(caretInFormattedText.Start, spanContainingFormattedSnippet.Start, caretInFormattedText.Length);
            lspTextChanges.Add(new TextChange(caretInSnippetContext, "$0"));
        }

        // Apply all the text changes to get the text formatted as the LSP snippet syntax.
        var formattedLspSnippetText = formattedText.GetSubText(spanContainingFormattedSnippet).WithChanges(lspTextChanges);

        return(formattedLspSnippetText.ToString());
Exemplo n.º 17
0
        public async Task <SnippetFunctionPart> WithSnippetFunctionResultAsync(Document documentWithSnippet, TextSpan fieldSpan, SimplifierOptions simplifierOptions, CancellationToken cancellationToken)
        {
            var snippetFunctionService = documentWithSnippet.Project.GetRequiredLanguageService <SnippetFunctionService>();

            switch (FunctionName)
            {
            case "SimpleTypeName":
                if (FunctionParam == null)
                {
                    return(this);
                }

                var simplifiedTypeName = await SnippetFunctionService.GetSimplifiedTypeNameAsync(documentWithSnippet, fieldSpan, FunctionParam, simplifierOptions, cancellationToken).ConfigureAwait(false);

                if (simplifiedTypeName == null)
                {
                    return(this);
                }

                return(this with {
                    DefaultText = simplifiedTypeName
                });
Exemplo n.º 18
0
        public static async ValueTask <SimplifierOptions> GetSimplifierOptionsAsync(this Document document, SimplifierOptions?fallbackOptions, CancellationToken cancellationToken)
        {
            var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

            return(SimplifierOptions.Create(documentOptions, document.Project.Solution.Workspace.Services, fallbackOptions, document.Project.Language));
        }