private async Task <Document> WithAttributeAsync([NotNull] INamedTypeSymbol attribute,
                                                         [NotNull] Document document, [NotNull] SyntaxNode syntaxNode, CancellationToken cancellationToken)
        {
            DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);

            // Add NotNull/CanBeNull/ItemNotNull/ItemCanBeNull attribute.
            SyntaxNode attributeSyntax =
                editor.Generator.Attribute(editor.Generator.TypeExpression(attribute))
                .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation, NamespaceImportAnnotation);

            editor.AddAttribute(syntaxNode, attributeSyntax);
            Document documentWithAttribute = editor.GetChangedDocument();

            // Add namespace import.
            Document documentWithImport =
                await
                ImportAdder.AddImportsAsync(documentWithAttribute, NamespaceImportAnnotation, null,
                                            cancellationToken).ConfigureAwait(false);

            // Simplify and reformat all annotated nodes.
            Document simplified = await SimplifyAsync(documentWithImport, cancellationToken).ConfigureAwait(false);

            SyntaxNode formatted = await FormatAsync(simplified, cancellationToken).ConfigureAwait(false);

            return(simplified.WithSyntaxRoot(formatted));
        }
예제 #2
0
        private async Task TestAsync(string initialText, string importsAddedText, string simplifiedText, OptionSet options = null)
        {
            var doc = GetDocument(initialText);

            options = options ?? doc.Options;

            var imported = await ImportAdder.AddImportsAsync(doc, options);

            if (importsAddedText != null)
            {
                var formatted = await Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation, options);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(importsAddedText, actualText);
            }

            if (simplifiedText != null)
            {
                var reduced = await Simplifier.ReduceAsync(imported, options);

                var formatted = await Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation, options);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(simplifiedText, actualText);
            }
        }
예제 #3
0
        private async Task TestAsync(string initialText, string importsAddedText, string simplifiedText, bool safe, bool useSymbolAnnotations, Func <OptionSet, OptionSet> optionsTransform = null)
        {
            var doc = await GetDocument(initialText, useSymbolAnnotations);

            OptionSet options = await doc.GetOptionsAsync();

            if (optionsTransform != null)
            {
                options = optionsTransform(options);
            }

            var imported = useSymbolAnnotations
                ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(doc, safe, options)
                : await ImportAdder.AddImportsFromSyntaxesAsync(doc, safe, options);

            if (importsAddedText != null)
            {
                var formatted = await Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation, options);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(importsAddedText, actualText);
            }

            if (simplifiedText != null)
            {
                var reduced = await Simplifier.ReduceAsync(imported, options);

                var formatted = await Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation, options);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(simplifiedText, actualText);
            }
        }
예제 #4
0
        internal static async Task <Document> CleanupDocumentAsync(
            Document document, CancellationToken cancellationToken)
        {
            if (document.SupportsSyntaxTree)
            {
                var addImportOptions = await AddImportPlacementOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

                var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

                document = await ImportAdder.AddImportsFromSymbolAnnotationAsync(
                    document, Simplifier.AddImportsAnnotation, addImportOptions, cancellationToken).ConfigureAwait(false);

                document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, cancellationToken : cancellationToken).ConfigureAwait(false);

                // format any node with explicit formatter annotation
                document = await Formatter.FormatAsync(document, Formatter.Annotation, formattingOptions, cancellationToken).ConfigureAwait(false);

                // format any elastic whitespace
                document = await Formatter.FormatAsync(document, SyntaxAnnotation.ElasticAnnotation, formattingOptions, cancellationToken).ConfigureAwait(false);

                document = await CaseCorrector.CaseCorrectAsync(document, CaseCorrector.Annotation, cancellationToken).ConfigureAwait(false);
            }

            return(document);
        }
예제 #5
0
        internal static async Task <Document> ConvertProperty(Document document, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken)
        {
            var editor = await DocumentEditor.CreateAsync(document, cancellationToken);

            var semanticModel = editor.SemanticModel;
            var fieldSymbol   = semanticModel.GetDeclaredSymbol(declarator);

            var initializer = (InvocationExpressionSyntax)declarator.Initializer.Value;
            var originalRegisterMethodName = ((MemberAccessExpressionSyntax)initializer.Expression).Name.ToString();

            var avaloniaInvocation = GenerateBasicInvocation(editor.Generator, fieldSymbol, initializer, originalRegisterMethodName);

            var originalStaticConstructor = fieldSymbol.ContainingType.StaticConstructors.IsEmpty || fieldSymbol.ContainingType.StaticConstructors[0].DeclaringSyntaxReferences.IsEmpty ? null :
                                            (ConstructorDeclarationSyntax)await fieldSymbol.ContainingType.StaticConstructors[0].DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken);
            var staticConstructor = originalStaticConstructor == null?GenerateEmptyStaticConstructor(editor.Generator, fieldSymbol.ContainingType) : originalStaticConstructor;

            ExpressionSyntax coerceCallbackSyntax     = null;
            ExpressionSyntax validationCallbackSyntax = null;
            var changeList = new ConverterProcessingResult();

            if (initializer.ArgumentList.Arguments.Count > 3) // Have to break down metadata constructor
            {
                var results = await ProcessMetadata(editor.Generator, semanticModel, fieldSymbol, cancellationToken);

                changeList           = changeList.AppendResult(results.Item1);
                coerceCallbackSyntax = results.Item2;
            }
            if (initializer.ArgumentList.Arguments.Count > 4)
            {
                validationCallbackSyntax = initializer.ArgumentList.Arguments[4].Expression;
            }

            if (coerceCallbackSyntax != null || validationCallbackSyntax != null)
            {
                var combinedCoerceValidateExpression = CreateCombinedCoerceValidate(editor.Generator, semanticModel, coerceCallbackSyntax, validationCallbackSyntax);
                changeList = changeList.AddArguments((ArgumentSyntax)editor.Generator.Argument("validate", RefKind.None, combinedCoerceValidateExpression));
            }
            avaloniaInvocation = avaloniaInvocation.AddArgumentListArguments(changeList.AdditionalInvocationArguments.ToArray())
                                 .WithAdditionalAnnotations(Formatter.Annotation);
            ReplaceMember(editor, semanticModel, declarator, avaloniaInvocation);
            staticConstructor = staticConstructor.AddBodyStatements(changeList.AdditionalStaticConstructorStatements.ToArray())
                                .WithAdditionalAnnotations(Formatter.Annotation);
            if (originalStaticConstructor != null)
            {
                if (originalStaticConstructor.Body.Statements.Count < staticConstructor.Body.Statements.Count)
                {
                    editor.ReplaceNode(originalStaticConstructor, staticConstructor);
                }
            }
            else if (staticConstructor.Body.Statements.Count > 0)
            {
                editor.AddMember(await fieldSymbol.ContainingType.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken), staticConstructor);
            }

            return(await ImportAdder.AddImportsAsync(editor.GetChangedDocument(), Annotations.NamespaceImportAnnotation, cancellationToken : cancellationToken));
        }
예제 #6
0
        private static async Task TestAsync(
            string initialText,
            string importsAddedText,
            string simplifiedText,
            bool useSymbolAnnotations,
            Func <OptionSet, OptionSet> optionsTransform = null,
            bool performCheck = true
            )
        {
            var doc = await GetDocument(initialText, useSymbolAnnotations);

            OptionSet options = await doc.GetOptionsAsync();

            if (optionsTransform != null)
            {
                options = optionsTransform(options);
            }

            var imported = useSymbolAnnotations
                ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(doc, options)
                : await ImportAdder.AddImportsFromSyntaxesAsync(doc, options);

            if (importsAddedText != null)
            {
                var formatted = await Formatter.FormatAsync(
                    imported,
                    SyntaxAnnotation.ElasticAnnotation,
                    options
                    );

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(importsAddedText, actualText);
            }

            if (simplifiedText != null)
            {
                var reduced = await Simplifier.ReduceAsync(imported, options);

                var formatted = await Formatter.FormatAsync(
                    reduced,
                    SyntaxAnnotation.ElasticAnnotation,
                    options
                    );

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(simplifiedText, actualText);
            }

            if (performCheck)
            {
                if (initialText == importsAddedText && importsAddedText == simplifiedText)
                {
                    throw new Exception($"use {nameof(TestNoImportsAddedAsync)}");
                }
            }
        }
예제 #7
0
            private async Task <Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType, SyntaxNode newType, CancellationToken cancellationToken)
            {
                var oldRoot = await _document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                var newDocument      = _document.WithSyntaxRoot(oldRoot.ReplaceNode(oldType, newType));
                var addImportOptions = await AddImportPlacementOptions.FromDocumentAsync(_document, cancellationToken).ConfigureAwait(false);

                newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync(newDocument, addImportOptions, cancellationToken).ConfigureAwait(false);

                return(newDocument);
            }
            private async Task <Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType, SyntaxNode newType, CancellationToken cancellationToken)
            {
                var oldRoot = await _document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                var newDocument = _document.WithSyntaxRoot(oldRoot.ReplaceNode(oldType, newType));

                // fallback options: https://github.com/dotnet/roslyn/issues/60794
                var addImportOptions = await _document.GetAddImportPlacementOptionsAsync(fallbackOptions : null, cancellationToken).ConfigureAwait(false);

                newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync(newDocument, addImportOptions, cancellationToken).ConfigureAwait(false);

                return(newDocument);
            }
예제 #9
0
        private static async Task TestAsync(
            string initialText,
            string importsAddedText,
            string simplifiedText,
            bool useSymbolAnnotations,
            bool placeSystemNamespaceFirst    = true,
            bool placeImportsInsideNamespaces = false,
            bool performCheck = true)
        {
            var doc = await GetDocument(initialText, useSymbolAnnotations);

            var addImportOptions = new AddImportPlacementOptions()
            {
                PlaceSystemNamespaceFirst = placeSystemNamespaceFirst,
                UsingDirectivePlacement   = new CodeStyleOption2 <AddImportPlacement>(placeImportsInsideNamespaces ? AddImportPlacement.InsideNamespace : AddImportPlacement.OutsideNamespace, NotificationOption2.None),
            };

            var formattingOptions = CSharpSyntaxFormattingOptions.Default;

            var simplifierOptions = CSharpSimplifierOptions.Default;

            var imported = useSymbolAnnotations
                ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(doc, addImportOptions, CancellationToken.None)
                : await ImportAdder.AddImportsFromSyntaxesAsync(doc, addImportOptions, CancellationToken.None);

            if (importsAddedText != null)
            {
                var formatted = await Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(importsAddedText, actualText);
            }

            if (simplifiedText != null)
            {
                var reduced = await Simplifier.ReduceAsync(imported, simplifierOptions, CancellationToken.None);

                var formatted = await Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(simplifiedText, actualText);
            }

            if (performCheck)
            {
                if (initialText == importsAddedText && importsAddedText == simplifiedText)
                {
                    throw new Exception($"use {nameof(TestNoImportsAddedAsync)}");
                }
            }
        }
        private static async Task <Document> AppendAfterAssignmentAsync(CodeFixContext context, StatementSyntax relativeTo, SyntaxNode presentArgument, CancellationToken cancellationToken)
        {
            DocumentEditor editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken);

            Document   document = context.Document;
            SyntaxNode root     = await document.GetSyntaxRootAsync(cancellationToken);

            SyntaxNode assumesStatement = CreateAssumesPresentStatement(editor.Generator, presentArgument);

            root     = root.InsertNodesAfter(relativeTo, new SyntaxNode[] { assumesStatement });
            document = document.WithSyntaxRoot(root);
            document = await ImportAdder.AddImportsAsync(document, Simplifier.Annotation, cancellationToken : cancellationToken);

            return(document);
        }
예제 #11
0
        private static async Task TestAsync(
            string initialText,
            string importsAddedText,
            string simplifiedText,
            bool useSymbolAnnotations,
            bool placeSystemNamespaceFirst    = true,
            bool placeImportsInsideNamespaces = false,
            bool performCheck = true)
        {
            var doc = await GetDocument(initialText, useSymbolAnnotations);

            var addImportOptions = new AddImportPlacementOptions(
                PlaceSystemNamespaceFirst: placeSystemNamespaceFirst,
                PlaceImportsInsideNamespaces: placeImportsInsideNamespaces,
                AllowInHiddenRegions: false);

            var imported = useSymbolAnnotations
                ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(doc, addImportOptions, CancellationToken.None)
                : await ImportAdder.AddImportsFromSyntaxesAsync(doc, addImportOptions, CancellationToken.None);

            if (importsAddedText != null)
            {
                var formatted = await Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(importsAddedText, actualText);
            }

            if (simplifiedText != null)
            {
                var reduced = await Simplifier.ReduceAsync(imported);

                var formatted = await Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation);

                var actualText = (await formatted.GetTextAsync()).ToString();
                Assert.Equal(simplifiedText, actualText);
            }

            if (performCheck)
            {
                if (initialText == importsAddedText && importsAddedText == simplifiedText)
                {
                    throw new Exception($"use {nameof(TestNoImportsAddedAsync)}");
                }
            }
        }
        private async Task <Document> GetEditAsync(
            Solution solution,
            INamespaceOrTypeSymbol destination,
            Func <SyntaxNode, CodeGenerationOptions, IList <bool>?, CancellationToken, SyntaxNode> declarationTransform,
            CodeGenerationOptions?options,
            CancellationToken cancellationToken)
        {
            options ??= CodeGenerationOptions.Default;

            var(destinationDeclaration, availableIndices) =
                await this.FindMostRelevantDeclarationAsync(solution, destination, options, cancellationToken).ConfigureAwait(false);

            if (destinationDeclaration == null)
            {
                throw new ArgumentException(WorkspacesResources.Could_not_find_location_to_generation_symbol_into);
            }

            var destinationTree = destinationDeclaration.SyntaxTree;
            var oldDocument     = solution.GetRequiredDocument(destinationTree);

            if (options.Options is null)
            {
                var documentOptions = await oldDocument.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

                options = options.With(options: documentOptions);
            }

            var transformedDeclaration = declarationTransform(destinationDeclaration, options, availableIndices, cancellationToken);

            var root = await destinationTree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            var currentRoot = root.ReplaceNode(destinationDeclaration, transformedDeclaration);

            var newDocument = oldDocument.WithSyntaxRoot(currentRoot);

            if (options.AddImports)
            {
                newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync(
                    newDocument,
                    await newDocument.GetOptionsAsync(cancellationToken).ConfigureAwait(false),
                    cancellationToken).ConfigureAwait(false);
            }

            return(newDocument);
        }
예제 #13
0
        internal static async Task <Document> CleanupDocumentAsync(
            Document document, CodeCleanupOptions options, CancellationToken cancellationToken)
        {
            document = await ImportAdder.AddImportsFromSymbolAnnotationAsync(
                document, Simplifier.AddImportsAnnotation, options.AddImportOptions, cancellationToken).ConfigureAwait(false);

            document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, options.SimplifierOptions, cancellationToken).ConfigureAwait(false);

            // format any node with explicit formatter annotation
            document = await Formatter.FormatAsync(document, Formatter.Annotation, options.FormattingOptions, cancellationToken).ConfigureAwait(false);

            // format any elastic whitespace
            document = await Formatter.FormatAsync(document, SyntaxAnnotation.ElasticAnnotation, options.FormattingOptions, cancellationToken).ConfigureAwait(false);

            document = await CaseCorrector.CaseCorrectAsync(document, CaseCorrector.Annotation, cancellationToken).ConfigureAwait(false);

            return(document);
        }
예제 #14
0
        /// <summary>
        /// Replaces a syntax node with a new fully qualified name or member accession expression from a given string.
        /// </summary>
        /// <param name="document">The document to update.</param>
        /// <param name="node">The syntax node to be replaced.</param>
        /// <param name="newIdentifier">A string representation of the new identifier (name of member access expression) to be used in the document.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>An updated document with the given node replaced with the new identifier.</returns>
        private static async Task <Document> UpdateIdentifierTypeAsync(Document document, SyntaxNode node, string newIdentifier, CancellationToken cancellationToken)
        {
            var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);

            // Create new identifier
            var updatedNode = GetUpdatedNode(node, editor.Generator, newIdentifier)
                              .WithAdditionalAnnotations(Simplifier.Annotation, Simplifier.AddImportsAnnotation)
                              .WithTriviaFrom(node);

            editor.ReplaceNode(node, updatedNode);

            var updatedDocument = editor.GetChangedDocument();

            // Add using declaration if needed
            updatedDocument = await ImportAdder.AddImportsAsync(updatedDocument, Simplifier.AddImportsAnnotation, null, cancellationToken).ConfigureAwait(false);

            // Simplify the call, if possible
            updatedDocument = await Simplifier.ReduceAsync(updatedDocument, Simplifier.Annotation, null, cancellationToken).ConfigureAwait(false);

            return(updatedDocument);
        }
예제 #15
0
        private static async Task <Document> UpdateMemberAccessAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
        {
            var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);

            if (editor.OriginalRoot is not CompilationUnitSyntax documentRoot)
            {
                return(document);
            }

            // Replace the member access expression with Debugger.IsAttached
            var newExpression = SyntaxFactory.ParseExpression(DebuggerIsAttachedSyntax)
                                .WithTriviaFrom(node)
                                .WithAdditionalAnnotations(Simplifier.Annotation, Simplifier.AddImportsAnnotation);

            documentRoot = documentRoot.ReplaceNode(node, newExpression) !;
            editor.ReplaceNode(editor.OriginalRoot, documentRoot);
            var updatedDocument = editor.GetChangedDocument();

            // Add using declaration if needed
            updatedDocument = await ImportAdder.AddImportsAsync(updatedDocument, Simplifier.AddImportsAnnotation, null, cancellationToken).ConfigureAwait(false);

            return(updatedDocument);
        }
예제 #16
0
        private void Test(string initialText, string importsAddedText, string simplifiedText, OptionSet options = null)
        {
            var doc = GetDocument(initialText);

            options = options ?? doc.Project.Solution.Workspace.Options;

            var imported = ImportAdder.AddImportsAsync(doc, options).Result;

            if (importsAddedText != null)
            {
                var formatted  = Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation, options).Result;
                var actualText = formatted.GetTextAsync().Result.ToString();
                Assert.Equal(importsAddedText, actualText);
            }

            if (simplifiedText != null)
            {
                var reduced   = Simplifier.ReduceAsync(imported, options).Result;
                var formatted = Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation, options).Result;

                var actualText = formatted.GetTextAsync().Result.ToString();
                Assert.Equal(simplifiedText, actualText);
            }
        }
        private async Task <Document> GetEditAsync(
            CodeGenerationSolutionContext context,
            INamespaceOrTypeSymbol destination,
            Func <SyntaxNode, TCodeGenerationContextInfo, IList <bool>?, CancellationToken, SyntaxNode> declarationTransform,
            CancellationToken cancellationToken)
        {
            var(destinationDeclaration, availableIndices) =
                await FindMostRelevantDeclarationAsync(context.Solution, destination, context.Context.BestLocation, cancellationToken).ConfigureAwait(false);

            if (destinationDeclaration == null)
            {
                throw new ArgumentException(WorkspacesResources.Could_not_find_location_to_generation_symbol_into);
            }

            var destinationTree = destinationDeclaration.SyntaxTree;
            var oldDocument     = context.Solution.GetRequiredDocument(destinationTree);
            var codeGenOptions  = await oldDocument.GetCodeGenerationOptionsAsync(context.FallbackOptions, cancellationToken).ConfigureAwait(false);

            var info = (TCodeGenerationContextInfo)codeGenOptions.GetInfo(context.Context, destinationDeclaration.SyntaxTree.Options);
            var transformedDeclaration = declarationTransform(destinationDeclaration, info, availableIndices, cancellationToken);

            var root = await destinationTree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            var currentRoot = root.ReplaceNode(destinationDeclaration, transformedDeclaration);

            var newDocument = oldDocument.WithSyntaxRoot(currentRoot);

            if (context.Context.AddImports)
            {
                var addImportsOptions = await newDocument.GetAddImportPlacementOptionsAsync(context.FallbackOptions, cancellationToken).ConfigureAwait(false);

                newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync(newDocument, addImportsOptions, cancellationToken).ConfigureAwait(false);
            }

            return(newDocument);
        }
예제 #18
0
        public async Task TestDoNotAddDuplicateImportIfNamespaceIsDefinedInSourceAndExternalAssembly(bool useSymbolAnnotations)
        {
            var externalCode =
                @"namespace N.M { public class A : System.Attribute { } }";

            var code =
                @"using System;
using N.M;

class C
{
    public void M1(String p1) { }

    public void M2([A] String p2) { }
}";

            var otherAssemblyReference = GetInMemoryAssemblyReferenceForCode(externalCode);

            var ws           = new AdhocWorkspace();
            var emptyProject = ws.AddProject(
                ProjectInfo.Create(
                    ProjectId.CreateNewId(),
                    VersionStamp.Default,
                    "test",
                    "test.dll",
                    LanguageNames.CSharp,
                    metadataReferences: new[] { TestMetadata.Net451.mscorlib }));

            var project = emptyProject
                          .AddMetadataReferences(new[] { otherAssemblyReference })
                          .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            project = project.AddDocument("duplicate.cs", externalCode).Project;
            var document = project.AddDocument("test.cs", code);

            var compilation = await document.Project.GetCompilationAsync(CancellationToken.None);

            var compilerDiagnostics = compilation.GetDiagnostics(CancellationToken.None);

            Assert.Empty(compilerDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error));

            var attribute = compilation.GetTypeByMetadataName("N.M.A");

            var syntaxRoot = await document.GetSyntaxRootAsync(CancellationToken.None).ConfigureAwait(false);

            SyntaxNode p1SyntaxNode = syntaxRoot.DescendantNodes().OfType <ParameterSyntax>().FirstOrDefault();

            // Add N.M.A attribute to p1.
            var editor = await DocumentEditor.CreateAsync(document, CancellationToken.None).ConfigureAwait(false);

            var attributeSyntax = editor.Generator.Attribute(editor.Generator.TypeExpression(attribute));

            editor.AddAttribute(p1SyntaxNode, attributeSyntax);
            var documentWithAttribute = editor.GetChangedDocument();

            var addImportOptions  = new AddImportPlacementOptions();
            var formattingOptions = CSharpSyntaxFormattingOptions.Default;

            // Add namespace import.
            var imported = useSymbolAnnotations
                ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(documentWithAttribute, addImportOptions, CancellationToken.None).ConfigureAwait(false)
                : await ImportAdder.AddImportsFromSyntaxesAsync(documentWithAttribute, addImportOptions, CancellationToken.None).ConfigureAwait(false);

            var formatted = await Formatter.FormatAsync(imported, formattingOptions, CancellationToken.None);

            var actualText = (await formatted.GetTextAsync()).ToString();

            Assert.Equal(@"using System;
using N.M;

class C
{
    public void M1([global::N.M.A] String p1) { }

    public void M2([A] String p2) { }
}", actualText);
        }
예제 #19
0
        private async Task <Document> ConvertToAsyncPackageAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var compilation = await context.Document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

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

            var baseTypeSyntax         = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <BaseTypeSyntax>();
            var classDeclarationSyntax = baseTypeSyntax.FirstAncestorOrSelf <ClassDeclarationSyntax>();
            var initializeMethodSyntax = classDeclarationSyntax.DescendantNodes()
                                         .OfType <MethodDeclarationSyntax>()
                                         .FirstOrDefault(method => method.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.OverrideKeyword)) && method.Identifier.Text == Types.Package.Initialize);
            var baseInitializeInvocationSyntax = initializeMethodSyntax?.Body?.DescendantNodes()
                                                 .OfType <InvocationExpressionSyntax>()
                                                 .FirstOrDefault(ies => ies.Expression is MemberAccessExpressionSyntax memberAccess && memberAccess.Name?.Identifier.Text == Types.Package.Initialize && memberAccess.Expression is BaseExpressionSyntax);
            var             getServiceInvocationsSyntax = new List <InvocationExpressionSyntax>();
            AttributeSyntax packageRegistrationSyntax   = null;

            {
                var userClassSymbol             = semanticModel.GetDeclaredSymbol(classDeclarationSyntax, context.CancellationToken);
                var packageRegistrationType     = compilation.GetTypeByMetadataName(Types.PackageRegistrationAttribute.FullName);
                var packageRegistrationInstance = userClassSymbol?.GetAttributes().FirstOrDefault(a => a.AttributeClass == packageRegistrationType);
                if (packageRegistrationInstance?.ApplicationSyntaxReference != null)
                {
                    packageRegistrationSyntax = (AttributeSyntax)await packageRegistrationInstance.ApplicationSyntaxReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
                }
            }

            if (initializeMethodSyntax != null)
            {
                getServiceInvocationsSyntax.AddRange(
                    from invocation in initializeMethodSyntax.DescendantNodes().OfType <InvocationExpressionSyntax>()
                    let memberBinding = invocation.Expression as MemberAccessExpressionSyntax
                                        let identifierName = invocation.Expression as IdentifierNameSyntax
                                                             where identifierName?.Identifier.Text == Types.Package.GetService ||
                                                             (memberBinding.Name.Identifier.Text == Types.Package.GetService && memberBinding.Expression.IsKind(SyntaxKind.ThisExpression))
                                                             select invocation);
            }

            // Make it easier to track nodes across changes.
            var nodesToTrack = new List <SyntaxNode>
            {
                baseTypeSyntax,
                initializeMethodSyntax,
                baseInitializeInvocationSyntax,
                packageRegistrationSyntax,
            };

            nodesToTrack.AddRange(getServiceInvocationsSyntax);
            nodesToTrack.RemoveAll(n => n == null);
            var updatedRoot = root.TrackNodes(nodesToTrack);

            // Replace the Package base type with AsyncPackage
            baseTypeSyntax = updatedRoot.GetCurrentNode(baseTypeSyntax);
            var asyncPackageBaseTypeSyntax = SyntaxFactory.SimpleBaseType(Types.AsyncPackage.TypeSyntax.WithAdditionalAnnotations(Simplifier.Annotation))
                                             .WithLeadingTrivia(baseTypeSyntax.GetLeadingTrivia())
                                             .WithTrailingTrivia(baseTypeSyntax.GetTrailingTrivia());

            updatedRoot = updatedRoot.ReplaceNode(baseTypeSyntax, asyncPackageBaseTypeSyntax);

            // Update the PackageRegistration attribute
            if (packageRegistrationSyntax != null)
            {
                var trueExpression = SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression);
                packageRegistrationSyntax = updatedRoot.GetCurrentNode(packageRegistrationSyntax);
                var allowsBackgroundLoadingSyntax = packageRegistrationSyntax.ArgumentList.Arguments.FirstOrDefault(a => a.NameEquals?.Name?.Identifier.Text == Types.PackageRegistrationAttribute.AllowsBackgroundLoading);
                if (allowsBackgroundLoadingSyntax != null)
                {
                    updatedRoot = updatedRoot.ReplaceNode(
                        allowsBackgroundLoadingSyntax,
                        allowsBackgroundLoadingSyntax.WithExpression(trueExpression));
                }
                else
                {
                    updatedRoot = updatedRoot.ReplaceNode(
                        packageRegistrationSyntax,
                        packageRegistrationSyntax.AddArgumentListArguments(
                            SyntaxFactory.AttributeArgument(trueExpression).WithNameEquals(SyntaxFactory.NameEquals(Types.PackageRegistrationAttribute.AllowsBackgroundLoading))));
                }
            }

            // Find the Initialize override, if present, and update it to InitializeAsync
            if (initializeMethodSyntax != null)
            {
                var cancellationTokenLocalVarName = SyntaxFactory.IdentifierName("cancellationToken");
                var progressLocalVarName          = SyntaxFactory.IdentifierName("progress");
                initializeMethodSyntax = updatedRoot.GetCurrentNode(initializeMethodSyntax);
                var newBody = initializeMethodSyntax.Body;

                var leadingTrivia = SyntaxFactory.TriviaList(
                    SyntaxFactory.Comment(@"// When initialized asynchronously, we *may* be on a background thread at this point."),
                    SyntaxFactory.CarriageReturnLineFeed,
                    SyntaxFactory.Comment(@"// Do any initialization that requires the UI thread after switching to the UI thread."),
                    SyntaxFactory.CarriageReturnLineFeed,
                    SyntaxFactory.Comment(@"// Otherwise, remove the switch to the UI thread if you don't need it."),
                    SyntaxFactory.CarriageReturnLineFeed);

                var switchToMainThreadStatement = SyntaxFactory.ExpressionStatement(
                    SyntaxFactory.AwaitExpression(
                        SyntaxFactory.InvocationExpression(
                            SyntaxFactory.MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                SyntaxFactory.MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    SyntaxFactory.ThisExpression(),
                                    SyntaxFactory.IdentifierName(Types.ThreadHelper.JoinableTaskFactory)),
                                SyntaxFactory.IdentifierName(Types.JoinableTaskFactory.SwitchToMainThreadAsync)))
                        .AddArgumentListArguments(SyntaxFactory.Argument(cancellationTokenLocalVarName))))
                                                  .WithLeadingTrivia(leadingTrivia)
                                                  .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed);

                if (baseInitializeInvocationSyntax != null)
                {
                    var baseInitializeAsyncInvocationBookmark = new SyntaxAnnotation();
                    var baseInitializeAsyncInvocationSyntax   = SyntaxFactory.AwaitExpression(
                        baseInitializeInvocationSyntax
                        .WithLeadingTrivia()
                        .WithExpression(
                            SyntaxFactory.MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                SyntaxFactory.BaseExpression(),
                                SyntaxFactory.IdentifierName(Types.AsyncPackage.InitializeAsync)))
                        .AddArgumentListArguments(
                            SyntaxFactory.Argument(cancellationTokenLocalVarName),
                            SyntaxFactory.Argument(progressLocalVarName)))
                                                                .WithLeadingTrivia(baseInitializeInvocationSyntax.GetLeadingTrivia())
                                                                .WithAdditionalAnnotations(baseInitializeAsyncInvocationBookmark);
                    newBody = newBody.ReplaceNode(initializeMethodSyntax.GetCurrentNode(baseInitializeInvocationSyntax), baseInitializeAsyncInvocationSyntax);
                    var baseInvocationStatement = newBody.GetAnnotatedNodes(baseInitializeAsyncInvocationBookmark).First().FirstAncestorOrSelf <StatementSyntax>();

                    newBody = newBody.InsertNodesAfter(
                        baseInvocationStatement,
                        new[] { switchToMainThreadStatement.WithLeadingTrivia(switchToMainThreadStatement.GetLeadingTrivia().Insert(0, SyntaxFactory.LineFeed)) });
                }
                else
                {
                    newBody = newBody.WithStatements(
                        newBody.Statements.Insert(0, switchToMainThreadStatement));
                }

                var initializeAsyncMethodSyntax = initializeMethodSyntax
                                                  .WithIdentifier(SyntaxFactory.Identifier(Types.AsyncPackage.InitializeAsync))
                                                  .WithReturnType(Types.Task.TypeSyntax.WithAdditionalAnnotations(Simplifier.Annotation))
                                                  .AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
                                                  .AddParameterListParameters(
                    SyntaxFactory.Parameter(cancellationTokenLocalVarName.Identifier).WithType(Types.CancellationToken.TypeSyntax.WithAdditionalAnnotations(Simplifier.Annotation)),
                    SyntaxFactory.Parameter(progressLocalVarName.Identifier).WithType(Types.IProgress.TypeSyntaxOf(Types.ServiceProgressData.TypeSyntax).WithAdditionalAnnotations(Simplifier.Annotation)))
                                                  .WithBody(newBody);
                updatedRoot = updatedRoot.ReplaceNode(initializeMethodSyntax, initializeAsyncMethodSyntax);

                // Replace GetService calls with GetServiceAsync
                getServiceInvocationsSyntax = updatedRoot.GetCurrentNodes <InvocationExpressionSyntax>(getServiceInvocationsSyntax).ToList();
                updatedRoot = updatedRoot.ReplaceNodes(
                    getServiceInvocationsSyntax,
                    (orig, node) =>
                {
                    var invocation = node;
                    if (invocation.Expression is IdentifierNameSyntax methodName)
                    {
                        invocation = invocation.WithExpression(SyntaxFactory.IdentifierName(Types.AsyncPackage.GetServiceAsync));
                    }
                    else if (invocation.Expression is MemberAccessExpressionSyntax memberAccess)
                    {
                        invocation = invocation.WithExpression(
                            memberAccess.WithName(SyntaxFactory.IdentifierName(Types.AsyncPackage.GetServiceAsync)));
                    }

                    return(SyntaxFactory.ParenthesizedExpression(SyntaxFactory.AwaitExpression(invocation))
                           .WithAdditionalAnnotations(Simplifier.Annotation));
                });

                updatedRoot = await Utils.AddUsingTaskEqualsDirectiveAsync(updatedRoot, cancellationToken);
            }

            var newDocument = context.Document.WithSyntaxRoot(updatedRoot);

            newDocument = await ImportAdder.AddImportsAsync(newDocument, Simplifier.Annotation, cancellationToken : cancellationToken);

            return(newDocument);
        }
예제 #20
0
        private async Task <Document> ConvertType(Document document, SyntaxNode type, CancellationToken c)
        {
            var editor = await DocumentEditor.CreateAsync(document, c);

            if (type is BaseTypeSyntax)
            {
                type = ((BaseTypeSyntax)type).Type;
            }
            var        originalTypeSymbol = editor.SemanticModel.GetTypeInfo(type, c).Type;
            SyntaxNode newTypeSyntax      = null;
            var        avaloniaNamespace  = editor.Generator.IdentifierName("Avalonia")
                                            .WithAdditionalAnnotations(Annotations.NamespaceImportAnnotation);
            var avaloniaControlsNamespace = editor.Generator.MemberAccessExpression(avaloniaNamespace, "Controls")
                                            .WithAdditionalAnnotations(Annotations.NamespaceImportAnnotation);
            var avaloniaControlsPrimitivesNamespace = editor.Generator.MemberAccessExpression(avaloniaControlsNamespace, "Primitives")
                                                      .WithAdditionalAnnotations(Annotations.NamespaceImportAnnotation);
            var avaloniaControlsShapesNamespace = editor.Generator.MemberAccessExpression(avaloniaControlsNamespace, "Shapes")
                                                  .WithAdditionalAnnotations(Annotations.NamespaceImportAnnotation);
            var avaloniaMediaNamespace = editor.Generator.MemberAccessExpression(avaloniaNamespace, "Media")
                                         .WithAdditionalAnnotations(Annotations.NamespaceImportAnnotation);

            if (originalTypeSymbol.ToDisplayString() == "System.Windows.DependencyObject")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaNamespace, "AvaloniaObject");
            }
            else if (originalTypeSymbol.ToDisplayString() == "System.Windows.DependencyProperty")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaNamespace, "AvaloniaProperty");
            }
            else if (originalTypeSymbol.ToDisplayString() == "System.Windows.UIElement" ||
                     originalTypeSymbol.ToDisplayString() == "System.Windows.FrameworkElement")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaControlsNamespace, "Control");
            }
            else if (originalTypeSymbol.ToDisplayString() == "System.Windows.Controls.Control")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaControlsPrimitivesNamespace, "TemplatedControl");
            }
            else if (originalTypeSymbol.ToDisplayString() == "System.Windows.Window")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaControlsNamespace, "Window");
            }
            else if (originalTypeSymbol.ContainingNamespace.ToDisplayString() == "System.Windows.Controls")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaControlsNamespace, originalTypeSymbol.Name);
            }
            else if (originalTypeSymbol.ContainingNamespace.ToDisplayString() == "System.Windows.Media")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaMediaNamespace, originalTypeSymbol.Name);
            }
            else if (originalTypeSymbol.ContainingNamespace.ToDisplayString() == "System.Windows.Shapes")
            {
                newTypeSyntax = editor.Generator.MemberAccessExpression(avaloniaControlsShapesNamespace, originalTypeSymbol.Name);
            }

            if (newTypeSyntax != null)
            {
                var newSymbol = editor.SemanticModel.GetSpeculativeTypeInfo(0, newTypeSyntax, SpeculativeBindingOption.BindAsExpression).Type;
                if (newSymbol != null)
                {
                    editor.ReplaceNode(type, editor.Generator.TypeExpression(newSymbol).WithAdditionalAnnotations(Formatter.Annotation));
                }
            }
            return(await ImportAdder.AddImportsAsync(editor.GetChangedDocument(), Annotations.NamespaceImportAnnotation, cancellationToken : c));
        }
예제 #21
0
        public async Task TestWarnsWithMatchingExtensionMethodUsedAsDelegate(bool useSymbolAnnotations)
        {
            var source = @"using System;
using B;

namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}

namespace B
{
    static class BExtensions
    {
        public static void M(this object a){}
    }
}

class C
{
    Action M(A.C1 c1) => 42.M;
}";

            await TestAsync(
                source,
                @"using System;
using A;
using B;

namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}

namespace B
{
    static class BExtensions
    {
        public static void M(this object a){}
    }
}

class C
{
    Action M(A.C1 c1) => 42.M;
}",

                @"using System;
using A;
using B;

namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}

namespace B
{
    static class BExtensions
    {
        public static void M(this object a){}
    }
}

class C
{
    Action M(C1 c1) => 42.M;
}", safe : true, useSymbolAnnotations);

            var doc = await GetDocument(source, useSymbolAnnotations);

            OptionSet options = await doc.GetOptionsAsync();

            var imported = await ImportAdder.AddImportsFromSyntaxesAsync(doc, true, options);

            var root = await imported.GetSyntaxRootAsync();

            var nodeWithWarning = root.GetAnnotatedNodes(WarningAnnotation.Kind).Single();

            Assert.Equal("42.M", nodeWithWarning.ToFullString());

            var warning = nodeWithWarning.GetAnnotations(WarningAnnotation.Kind).Single();

            Assert.Equal("Adding imports will bring an extension method into scope with the same name as 'M'", WarningAnnotation.GetDescription(warning));
        }
        /// TODO: this might be better as a visitor
        /// offer to break out the continuation into a success event handler the exception block into an error event handler
        /// TODO: hooking them up where other bus.AddHandlers are called
        /// and replace RequestAsync with Send
        /// with TODOs to verify storage of state and retrieval of state
        private static async Task <Solution> InvokeMp0102(Diagnostic diagnostic, Solution solution, Document document,
                                                          CancellationToken cancellationToken)
        {
            // the diagnostic.Location here will be the span of the RequestAsync<> GenericNameSyntax object

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

            var model = await document.GetSemanticModelAsync(cancellationToken);

            var generator = SyntaxGenerator.GetGenerator(document);

            MemberAccessExpressionSyntax requestAsyncMemberAccess;
            SyntaxNode requestAsyncInvocationStatement;

            StatementSyntax[] requestAsyncAndDependantStatements;
            string            fullContainingNamespaceName;
            SyntaxToken       handleMethodParameterName;
            var containingMemberAnnotation = new SyntaxAnnotation(Guid.NewGuid().ToString("D"));
            var tryAnnotation         = new SyntaxAnnotation(Guid.NewGuid().ToString("D"));
            var subjectNodeAnnotation = new SyntaxAnnotation(Guid.NewGuid().ToString("D"));
            INamedTypeSymbol  errorEventType;
            INamedTypeSymbol  eventType;
            BlockSyntax       eventHandlerHandleMethodBody;
            SyntaxToken       catchExceptionIdentifier = default(SyntaxToken);
            CatchClauseSyntax catchStatement           = null;
            SyntaxToken       errorEventHandlerMessageParameterIdentifier;
            {
                var subjectNode = root.FindNode(diagnostic.Location.SourceSpan);
                requestAsyncMemberAccess = subjectNode.Parent.AncestorsAndSelf().OfType <MemberAccessExpressionSyntax>().First();

                INamedTypeSymbol messageType;
                Utilities.GetRequestAsyncInfo(requestAsyncMemberAccess, model, out messageType, out eventType, out errorEventType);

                // Get span of code around RequestAsync for event handler
                handleMethodParameterName = Identifier(
                    requestAsyncMemberAccess.GetAssignmentSymbol(model, cancellationToken).Name);

                document = document.ReplaceNode(subjectNode, subjectNode.WithAdditionalAnnotations(subjectNodeAnnotation), out root,
                                                out model);
                subjectNode = root.GetAnnotatedNodes(subjectNodeAnnotation).Single();

                var containingMember = subjectNode.GetContainingMemberDeclaration() as MethodDeclarationSyntax;
                Debug.Assert(containingMember != null, "containingMember != null");
                fullContainingNamespaceName =
                    // ReSharper disable once PossibleNullReferenceException
                    model.GetDeclaredSymbol(containingMember.Parent, cancellationToken).ContainingNamespace.GetFullNamespaceName();

                document = document.ReplaceNode(containingMember,
                                                containingMember
                                                .WithAdditionalAnnotations(containingMemberAnnotation)
                                                .WithAdditionalAnnotations(Formatter.Annotation),
                                                out root, out model);
                containingMember = (MethodDeclarationSyntax)root.GetAnnotatedNodes(containingMemberAnnotation).Single();
                subjectNode      = root.GetAnnotatedNodes(subjectNodeAnnotation).Single();
                requestAsyncInvocationStatement = subjectNode.GetAncestorStatement();
                var eventHandlerStatementsSpan =
                    containingMember.GetSpanOfAssignmentDependenciesAndDeclarationsInSpan(requestAsyncInvocationStatement.FullSpan,
                                                                                          model);

                // Get catch block, and create error event handler
                // while ancestor parent is not try or member declaration, if try, check for correct catch.
                // if none found, throw
                {
                    var tryCandidate = requestAsyncInvocationStatement.Ancestors().First(e => e is BlockSyntax).Parent;
                    do
                    {
                        var tryStatement = tryCandidate as TryStatementSyntax;
                        if (tryStatement != null)
                        {
                            var exceptionType = typeof(ReceivedErrorEventException <>);
                            catchStatement = tryStatement.GetFirstCatchClauseByType(model, exceptionType, cancellationToken);
                            if (catchStatement != null)
                            {
                                var errorType = model.GetTypeInfo(catchStatement.Declaration.Type, cancellationToken).Type as INamedTypeSymbol;
                                // ReSharper disable once PossibleNullReferenceException
                                if (errorEventType.ToString().Equals(errorType.TypeArguments[0].ToString()))
                                {
                                    catchExceptionIdentifier = catchStatement.Declaration.Identifier;
                                    break;
                                }
                                catchStatement = null;
                            }
                        }
                        tryCandidate = tryCandidate.Parent;
                    } while (tryCandidate != null && !(tryCandidate is MemberDeclarationSyntax));

                    if (catchStatement == null)
                    {
                        throw new InvalidOperationException();
                    }

                    errorEventHandlerMessageParameterIdentifier = GenerateUniqueParameterIdentifierForScope(errorEventType, model,
                                                                                                            catchStatement.Block);
                    catchStatement = catchStatement.ReplaceNodes(
                        catchStatement.Block.DescendantNodes().Where(e => e is ExpressionSyntax),
                        (a, b) => Simplifier.Expand(a, model, document.Project.Solution.Workspace));

                    var firstStatement = catchStatement.Block.Statements.FirstOrDefault();
                    if (firstStatement != null)
                    {
                        catchStatement = catchStatement.ReplaceNode(firstStatement,
                                                                    firstStatement.WithLeadingTrivia(
                                                                        Comment("// TODO: Load message information from a repository by CorrelationId"),
                                                                        LineFeed));
                    }
                    else
                    {
                        catchStatement = catchStatement.WithBlock(catchStatement.Block.WithOpenBraceToken(
                                                                      Token(SyntaxKind.CloseBraceToken)
                                                                      .WithLeadingTrivia(Comment("// TODO: Load message information from a repository by CorrelationId"),
                                                                                         LineFeed)));
                    }

                    foreach (var statement in catchStatement.Block.DescendantNodes(_ => true)
                             .OfType <MemberAccessExpressionSyntax>()
                             .Where(a =>
                    {
                        var i = a.Expression as IdentifierNameSyntax;
                        return(i != null && i.Identifier.ValueText == catchExceptionIdentifier.ValueText);
                    })
                             .ToArray())
                    {
                        catchStatement = catchStatement.ReplaceNode(statement,
                                                                    IdentifierName(errorEventHandlerMessageParameterIdentifier));
                    }

                    document = document.ReplaceNode(tryCandidate, tryCandidate.WithAdditionalAnnotations(tryAnnotation), out root,
                                                    out model);
                }
                subjectNode = root.GetAnnotatedNodes(subjectNodeAnnotation).Single();
                requestAsyncInvocationStatement    = subjectNode.GetAncestorStatement();
                requestAsyncAndDependantStatements = root.DescendantNodes()
                                                     .OfType <StatementSyntax>()
                                                     .Where(x => eventHandlerStatementsSpan.Contains(x.Span) && x != requestAsyncInvocationStatement)
                                                     .ToArray();
                // expand in original document and add to block
                eventHandlerHandleMethodBody = Block(List(requestAsyncAndDependantStatements
                                                          .Select(e => Simplifier.Expand(e, model, document.Project.Solution.Workspace))
                                                          .ToArray()));
                eventHandlerHandleMethodBody = eventHandlerHandleMethodBody.ReplaceNode(eventHandlerHandleMethodBody.Statements.First(),
                                                                                        eventHandlerHandleMethodBody.Statements.First().WithLeadingTrivia(
                                                                                            Comment("// TODO: Load message information from a repository by CorrelationId"),
                                                                                            LineFeed));
            }

            var options = document.Project.Solution.Workspace.Options;
            var namespaceIdentifierName = IdentifierName(fullContainingNamespaceName);

            // start of modifications
            ClassDeclarationSyntax eventHandlerDeclaration;
            {
                #region create event handler declaration
                {
                    eventHandlerDeclaration = Utilities.MessageHandlerDeclaration(
                        eventType,
                        generator, eventHandlerHandleMethodBody,
                        handleMethodParameterName);

                    var ns = (NamespaceDeclarationSyntax)generator
                             .NamespaceDeclaration(namespaceIdentifierName)
                             .WithAdditionalAnnotations(Formatter.Annotation);

                    // create event handler document
                    var filename = eventHandlerDeclaration.Identifier.ValueText + ".cs";

                    // not thrilled about using text here, but some sort of disconnect between documents when
                    // we get to AddImports and Reduce otherwise.
                    var eventHandlerDocument = document.Project.AddDocument(filename,
                                                                            ns.AddMembers(eventHandlerDeclaration).NormalizeWhitespace().ToString());

                    eventHandlerDocument = await ImportAdder.AddImportsAsync(eventHandlerDocument, options, cancellationToken);

                    eventHandlerDocument = await Simplifier.ReduceAsync(eventHandlerDocument, options, cancellationToken);

                    eventHandlerDocument = await Formatter.FormatAsync(eventHandlerDocument, options, cancellationToken);

                    solution = eventHandlerDocument.Project.Solution.WithDocumentText(eventHandlerDocument.Id,
                                                                                      await eventHandlerDocument.GetTextAsync(cancellationToken));
                    document = solution.GetDocument(document.Id);
                    root     = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                    model = await document.GetSemanticModelAsync(cancellationToken);
                }

                #endregion create event handler declaration

                // replace the call to RequestAsync and dependant statements with call to Sendr
                root = root.ReplaceNodes(requestAsyncAndDependantStatements,
                                         ExpressionStatement(
                                             InvocationExpression(
                                                 MemberAccessExpression(
                                                     SyntaxKind.SimpleMemberAccessExpression,
                                                     requestAsyncMemberAccess.Expression,                            // "bus"
                                                     IdentifierName(nameof(BusExtensions.Send))
                                                     .WithAdditionalAnnotations(subjectNodeAnnotation)))
                                             .WithArgumentList(((InvocationExpressionSyntax)requestAsyncMemberAccess.Parent).ArgumentList))
                                         .WithLeadingTrivia(
                                             Comment("// TODO: store information about the message with CorrelationId for loading in handlers"),
                                             CarriageReturnLineFeed));

                // remove async and change return type
                var containingMember = (MethodDeclarationSyntax)root.GetAnnotatedNodes(containingMemberAnnotation).Single();
                root = root.ReplaceNode(containingMember, containingMember.WithoutAsync()
                                        .WithAdditionalAnnotations(Formatter.Annotation));
                // remove try/catch
                var @try = root.DescendantNodes(_ => true).Single(e => e.HasAnnotation(tryAnnotation));
                root = root.RemoveNode(@try, SyntaxRemoveOptions.KeepExteriorTrivia);

                document = document.WithSyntaxRoot(root);
            }

            // error event handler class:
            ClassDeclarationSyntax errorEventHandlerDeclaration;
            {
                errorEventHandlerDeclaration = Utilities.MessageHandlerDeclaration(
                    errorEventType,
                    generator, catchStatement.Block,
                    errorEventHandlerMessageParameterIdentifier);
                var ns = (NamespaceDeclarationSyntax)generator
                         .NamespaceDeclaration(namespaceIdentifierName)
                         .WithAdditionalAnnotations(Formatter.Annotation);
                // create new document
                var errorEventHandlerDocument =
                    document.Project.AddDocument(errorEventHandlerDeclaration.Identifier.ValueText + ".cs",
                                                 ns.AddMembers(errorEventHandlerDeclaration).NormalizeWhitespace().ToString());
                document = errorEventHandlerDocument.Project.GetDocument(document.Id);

                errorEventHandlerDocument = await ImportAdder.AddImportsAsync(errorEventHandlerDocument, options, cancellationToken);

                errorEventHandlerDocument = await Simplifier.ReduceAsync(errorEventHandlerDocument, options, cancellationToken);

                errorEventHandlerDocument = await Formatter.FormatAsync(errorEventHandlerDocument,
                                                                        cancellationToken : cancellationToken);

                solution = errorEventHandlerDocument.Project.Solution.WithDocumentText(errorEventHandlerDocument.Id,
                                                                                       await errorEventHandlerDocument.GetTextAsync(cancellationToken));
            }
            {
                document = solution.GetDocument(document.Id);
                model    = await document.GetSemanticModelAsync(cancellationToken);

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

                // go looking for a reference to c'tor of a IBus type.
                var busSend = root.GetAnnotatedNodes(subjectNodeAnnotation).Single();
                requestAsyncMemberAccess =
                    busSend.Parent.AncestorsAndSelf()
                    .OfType <MemberAccessExpressionSyntax>()
                    .First();
                var busSymbol = model.GetTypeInfo(requestAsyncMemberAccess.Expression).Type as INamedTypeSymbol;
                Debug.Assert(busSymbol != null, "busSymbol != null");
                IMethodSymbol ctorSymbol;
                // ReSharper disable once PossibleNullReferenceException
                if (busSymbol.TypeKind == TypeKind.Interface)
                {
                    var busImplementations = await SymbolFinder.FindImplementationsAsync(busSymbol, solution,
                                                                                         cancellationToken : cancellationToken);

                    foreach (INamedTypeSymbol impl in busImplementations.OfType <INamedTypeSymbol>())
                    {
                        // only implementations with public constructors
                        ctorSymbol = impl.Constructors.SingleOrDefault(e => !e.IsStatic && e.Parameters.Length == 0);
                        if (ctorSymbol != null)
                        {
                            busSymbol = impl;
                            break;
                        }
                    }
                }
                ctorSymbol = busSymbol.Constructors.SingleOrDefault(e => !e.IsStatic && e.Parameters.Length == 0);
                var handlerSymbol = busSymbol.GetMembers(nameof(IBus.AddHandler)).Single();
                // ReSharper disable once PossibleUnintendedReferenceComparison
                if (handlerSymbol != default(ISymbol))
                {
                    var references = (await SymbolFinder.FindReferencesAsync(handlerSymbol,
                                                                             solution, cancellationToken)).ToArray();
                    if (references.Any(e => e.Locations.Any()))
                    {
                        // TODO: add AddHandlers at this location
                        var definition = references
                                         .GroupBy(e => e.Definition)
                                         .OrderByDescending(g => g.Count())
                                         .First();
                    }
                    else
                    {
                        // no add handlers at the moment, let's just find where it was constructed.
                        var locations =
                            (await SymbolFinder.FindReferencesAsync(ctorSymbol, solution, cancellationToken))
                            .SelectMany(e => e.Locations).ToArray();
                        if (locations.Length != 0)
                        {
                            var referencedLocation = locations.First();
                            {
                                var referencedLocationDocument = referencedLocation.Document;
                                var referencedRoot             = await referencedLocationDocument.GetSyntaxRootAsync(cancellationToken);

                                var node        = referencedRoot.FindNode(referencedLocation.Location.SourceSpan);
                                var statement   = node.GetAncestorStatement();
                                var busNode     = statement.GetAssignmentToken().WithLeadingTrivia().WithTrailingTrivia();
                                var busNodeName = IdentifierName(busNode);
                                if (busNodeName != null)
                                {
                                    var errorEventHandlerName =
                                        IdentifierName(errorEventHandlerDeclaration.Identifier);
                                    referencedLocationDocument = InsertAddHandlerCall(
                                        referencedLocationDocument,
                                        referencedLocation, busNodeName, errorEventHandlerName);
                                    var eventHandlerName =
                                        IdentifierName(eventHandlerDeclaration.Identifier);
                                    referencedLocationDocument = InsertAddHandlerCall(
                                        referencedLocationDocument,
                                        referencedLocation, busNodeName, eventHandlerName);
                                    solution = referencedLocationDocument.Project.Solution;
                                }
                            }
                        }
                    }
                }
                return(solution);
            }
        }
예제 #23
0
        public async Task TestWarnsWithMatchingExtensionMethodUsedAsDelegate(bool useSymbolAnnotations)
        {
            var source = @"using System;
using B;

namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}

namespace B
{
    static class BExtensions
    {
        public static void M(this object a){}
    }
}

class C
{
    Action M(A.C1 c1) => 42.M;
}";

            await TestAsync(
                source,
                @"using System;
using A;
using B;

namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}

namespace B
{
    static class BExtensions
    {
        public static void M(this object a){}
    }
}

class C
{
    Action M(A.C1 c1) => 42.M;
}",

                @"using System;
using A;
using B;

namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}

namespace B
{
    static class BExtensions
    {
        public static void M(this object a){}
    }
}

class C
{
    Action M(C1 c1) => 42.M;
}", safe : true, useSymbolAnnotations);

            var doc = await GetDocument(source, useSymbolAnnotations);

            OptionSet options = await doc.GetOptionsAsync();

            var imported = await ImportAdder.AddImportsFromSyntaxesAsync(doc, true, options);

            var root = await imported.GetSyntaxRootAsync();

            var nodeWithWarning = root.GetAnnotatedNodes(WarningAnnotation.Kind).Single();

            Assert.Equal("42.M", nodeWithWarning.ToFullString());

            var warning = nodeWithWarning.GetAnnotations(WarningAnnotation.Kind).Single();
            var expectedWarningMessage = string.Format(WorkspacesResources.Warning_adding_imports_will_bring_an_extension_method_into_scope_with_the_same_name_as_member_access, "M");

            Assert.Equal(expectedWarningMessage, WarningAnnotation.GetDescription(warning));
        }
예제 #24
0
        public async Task TestDoNotAddDuplicateImportIfNamespaceIsDefinedInSourceAndExternalAssembly()
        {
            var externalCode =
                @"namespace N.M { public class A : System.Attribute { } }";

            var code =
                @"using System;
using N.M;

class C
{
    public void M1(String p1) { }

    public void M2([A] String p2) { }
}";

            var otherAssemblyReference = GetInMemoryAssemblyReferenceForCode(externalCode);

            var project = _emptyProject
                          .AddMetadataReferences(new[] { otherAssemblyReference })
                          .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            project = project.AddDocument("duplicate.cs", externalCode).Project;
            var document = project.AddDocument("test.cs", code);

            var options = document.Project.Solution.Workspace.Options;

            var compilation = await document.Project.Solution.GetCompilationAsync(document.Project, CancellationToken.None);

            ImmutableArray <Diagnostic> compilerDiagnostics = compilation.GetDiagnostics(CancellationToken.None);

            Assert.Empty(compilerDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error));

            var attribute = compilation.GetTypeByMetadataName("N.M.A");

            var syntaxRoot = await document.GetSyntaxRootAsync(CancellationToken.None).ConfigureAwait(false);

            SyntaxNode p1SyntaxNode = syntaxRoot.DescendantNodes().OfType <ParameterSyntax>().FirstOrDefault();

            // Add N.M.A attribute to p1.
            var editor = await DocumentEditor.CreateAsync(document, CancellationToken.None).ConfigureAwait(false);

            SyntaxNode attributeSyntax = editor.Generator.Attribute(editor.Generator.TypeExpression(attribute));

            editor.AddAttribute(p1SyntaxNode, attributeSyntax);
            Document documentWithAttribute = editor.GetChangedDocument();

            // Add namespace import.
            Document imported = await ImportAdder.AddImportsAsync(documentWithAttribute, null,
                                                                  CancellationToken.None).ConfigureAwait(false);

            var formatted = await Formatter.FormatAsync(imported, options);

            var actualText = (await formatted.GetTextAsync()).ToString();

            Assert.Equal(actualText,
                         @"using System;
using N.M;

class C
{
    public void M1([global::N.M.A] String p1) { }

    public void M2([A] String p2) { }
}");
        }