public SyntaxNode ExpandNode(SyntaxNode node, SyntaxNode root, SemanticModel semanticModel, Workspace workspace) { SyntaxNode expandedNode = Simplifier.Expand(node, semanticModel, workspace); return(WithoutGlobalOverqualification(node, expandedNode)); }
private async Task <Document> InlineTemporaryAsync(Document document, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken) { var workspace = document.Project.Solution.Workspace; // Annotate the variable declarator so that we can get back to it later. var updatedDocument = await document.ReplaceNodeAsync(declarator, declarator.WithAdditionalAnnotations(DefinitionAnnotation), cancellationToken).ConfigureAwait(false); var semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); // Create the expression that we're actually going to inline. var expressionToInline = await CreateExpressionToInlineAsync(variableDeclarator, updatedDocument, cancellationToken).ConfigureAwait(false); // Collect the identifier names for each reference. var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken); var symbolRefs = await SymbolFinder.FindReferencesAsync(local, updatedDocument.Project.Solution, cancellationToken).ConfigureAwait(false); var references = symbolRefs.Single(r => Equals(r.Definition, local)).Locations; var syntaxRoot = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Collect the topmost parenting expression for each reference. var nonConflictingIdentifierNodes = references .Select(loc => (IdentifierNameSyntax)syntaxRoot.FindToken(loc.Location.SourceSpan.Start).Parent) .Where(ident => !HasConflict(ident, variableDeclarator)); // Add referenceAnnotations to identifier nodes being replaced. updatedDocument = await updatedDocument.ReplaceNodesAsync( nonConflictingIdentifierNodes, (o, n) => n.WithAdditionalAnnotations(ReferenceAnnotation), cancellationToken).ConfigureAwait(false); semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); // Get the annotated reference nodes. nonConflictingIdentifierNodes = await FindReferenceAnnotatedNodesAsync(updatedDocument, cancellationToken).ConfigureAwait(false); var topmostParentingExpressions = nonConflictingIdentifierNodes .Select(ident => GetTopMostParentingExpression(ident)) .Distinct().ToList(); var originalInitializerSymbolInfo = semanticModel.GetSymbolInfo(variableDeclarator.Initializer.Value, cancellationToken); // Make each topmost parenting statement or Equals Clause Expressions semantically explicit. updatedDocument = await updatedDocument.ReplaceNodesAsync(topmostParentingExpressions, (o, n) => { var node = Simplifier.Expand(n, semanticModel, workspace, cancellationToken: cancellationToken); // warn when inlining into a conditional expression, as the inlined expression will not be executed. if (semanticModel.GetSymbolInfo(o).Symbol is IMethodSymbol { IsConditional : true }) { node = node.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_into_conditional_method_call)); } return(node); }, cancellationToken).ConfigureAwait(false);
public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node) { // We only care about xml doc comments var leadingTrivia = CanHaveDocComments(node) ? VisitList(node.GetLeadingTrivia()) : node.GetLeadingTrivia(); if (_namespaceMembers.Contains(node.Identifier.Text)) { var expanded = Simplifier.Expand <SyntaxNode>(node, _model, _workspace, cancellationToken: _cancellationToken); return(expanded.WithLeadingTrivia(leadingTrivia)); } return(node.WithLeadingTrivia(leadingTrivia)); }
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node) { // No need to visit trivia, as we only care about xml doc comments if (node.Expression is MemberAccessExpressionSyntax memberAccess) { if (_extensionMethods.Contains(memberAccess.Name.Identifier.Text)) { // No need to visit this as simplifier will expand everything return(Simplifier.Expand <SyntaxNode>(node, _model, _workspace, cancellationToken: _cancellationToken)); } } return(base.VisitInvocationExpression(node) ?? throw ExceptionUtilities.Unreachable); }
public SyntaxNode ExpandNode(SyntaxNode node, SyntaxNode root, SemanticModel semanticModel, Workspace workspace) { var symbol = semanticModel.GetSymbolInfo(node).Symbol; if (node is SimpleNameSyntax sns && IsMyBaseBug(node, symbol, root, semanticModel) && semanticModel.GetOperation(node) is IMemberReferenceOperation mro && mro.Instance != null) { var expressionSyntax = (ExpressionSyntax)mro.Instance.Syntax; return MemberAccess(expressionSyntax, sns); } if (node is MemberAccessExpressionSyntax maes && IsTypePromotion(node, symbol, root, semanticModel) && semanticModel.GetOperation(node) is IMemberReferenceOperation mro2 && mro2.Instance != null) { var expressionSyntax = (ExpressionSyntax)mro2.Instance.Syntax; return MemberAccess(expressionSyntax, SyntaxFactory.IdentifierName(mro2.Member.Name)); } return IsOriginalSymbolGenericMethod(semanticModel, node) ? node : Simplifier.Expand(node, semanticModel, workspace); }
public override SyntaxNode VisitGenericName(GenericNameSyntax node) { // We only care about xml doc comments var leadingTrivia = CanHaveDocComments(node) ? VisitList(node.GetLeadingTrivia()) : node.GetLeadingTrivia(); if (_namespaceMembers.Contains(node.Identifier.Text)) { // No need to visit type argument list as simplifier will expand everything var expanded = Simplifier.Expand <SyntaxNode>(node, _model, _workspace, cancellationToken: _cancellationToken); return(expanded.WithLeadingTrivia(leadingTrivia)); } var typeArgumentList = (TypeArgumentListSyntax)base.Visit(node.TypeArgumentList); return(node.Update(node.Identifier.WithLeadingTrivia(leadingTrivia), typeArgumentList)); }
private async Task <Document> InlineTemporaryAsync(Document document, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken) { var workspace = document.Project.Solution.Workspace; // Annotate the variable declarator so that we can get back to it later. var updatedDocument = await document.ReplaceNodeAsync(declarator, declarator.WithAdditionalAnnotations(DefinitionAnnotation), cancellationToken).ConfigureAwait(false); var semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); // Create the expression that we're actually going to inline. var expressionToInline = await CreateExpressionToInlineAsync(variableDeclarator, updatedDocument, cancellationToken).ConfigureAwait(false); // Collect the identifier names for each reference. var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken); var symbolRefs = await SymbolFinder.FindReferencesAsync(local, updatedDocument.Project.Solution, cancellationToken).ConfigureAwait(false); var references = symbolRefs.Single(r => Equals(r.Definition, local)).Locations; var syntaxRoot = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Collect the topmost parenting expression for each reference. var nonConflictingIdentifierNodes = references .Select(loc => (IdentifierNameSyntax)syntaxRoot.FindToken(loc.Location.SourceSpan.Start).Parent) .Where(ident => !HasConflict(ident, variableDeclarator)); // Add referenceAnnotations to identifier nodes being replaced. updatedDocument = await updatedDocument.ReplaceNodesAsync( nonConflictingIdentifierNodes, (o, n) => n.WithAdditionalAnnotations(ReferenceAnnotation), cancellationToken).ConfigureAwait(false); semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); // Get the annotated reference nodes. nonConflictingIdentifierNodes = await FindReferenceAnnotatedNodesAsync(updatedDocument, cancellationToken).ConfigureAwait(false); var topmostParentingExpressions = nonConflictingIdentifierNodes .Select(ident => GetTopMostParentingExpression(ident)) .Distinct(); var originalInitializerSymbolInfo = semanticModel.GetSymbolInfo(variableDeclarator.Initializer.Value, cancellationToken); // Make each topmost parenting statement or Equals Clause Expressions semantically explicit. updatedDocument = await updatedDocument.ReplaceNodesAsync(topmostParentingExpressions, (o, n) => Simplifier.Expand(n, semanticModel, workspace, cancellationToken: cancellationToken), cancellationToken).ConfigureAwait(false); semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var semanticModelBeforeInline = semanticModel; variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); var scope = GetScope(variableDeclarator); var newScope = ReferenceRewriter.Visit(semanticModel, scope, variableDeclarator, expressionToInline, cancellationToken); updatedDocument = await updatedDocument.ReplaceNodeAsync(scope, newScope, cancellationToken).ConfigureAwait(false); semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); newScope = GetScope(variableDeclarator); var conflicts = newScope.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); var declaratorConflicts = variableDeclarator.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); // Note that we only remove the local declaration if there weren't any conflicts, // unless those conflicts are inside the local declaration. if (conflicts.Count() == declaratorConflicts.Count()) { // Certain semantic conflicts can be detected only after the reference rewriter has inlined the expression var newDocument = await DetectSemanticConflicts(updatedDocument, semanticModel, semanticModelBeforeInline, originalInitializerSymbolInfo, cancellationToken).ConfigureAwait(false); if (updatedDocument == newDocument) { // No semantic conflicts, we can remove the definition. updatedDocument = await updatedDocument.ReplaceNodeAsync(newScope, RemoveDeclaratorFromScope(variableDeclarator, newScope), cancellationToken).ConfigureAwait(false); } else { // There were some semantic conflicts, don't remove the definition. updatedDocument = newDocument; } } return(updatedDocument); }
private static async Task <Document> InlineTemporaryAsync(Document document, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken) { var workspace = document.Project.Solution.Workspace; // Annotate the variable declarator so that we can get back to it later. var updatedDocument = await document.ReplaceNodeAsync(declarator, declarator.WithAdditionalAnnotations(DefinitionAnnotation), cancellationToken).ConfigureAwait(false); var semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); // Create the expression that we're actually going to inline. var expressionToInline = await CreateExpressionToInlineAsync(variableDeclarator, updatedDocument, cancellationToken).ConfigureAwait(false); // Collect the identifier names for each reference. var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken); var symbolRefs = await SymbolFinder.FindReferencesAsync(local, updatedDocument.Project.Solution, cancellationToken).ConfigureAwait(false); var referencedSymbol = symbolRefs.SingleOrDefault(r => Equals(r.Definition, local)); var references = referencedSymbol == null?SpecializedCollections.EmptyEnumerable <ReferenceLocation>() : referencedSymbol.Locations; var syntaxRoot = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Collect the topmost parenting expression for each reference. var nonConflictingIdentifierNodes = references .Select(loc => (IdentifierNameSyntax)syntaxRoot.FindToken(loc.Location.SourceSpan.Start).Parent) .Where(ident => !HasConflict(ident, variableDeclarator)); // Add referenceAnnotations to identifier nodes being replaced. updatedDocument = await updatedDocument.ReplaceNodesAsync( nonConflictingIdentifierNodes, (o, n) => n.WithAdditionalAnnotations(ReferenceAnnotation), cancellationToken).ConfigureAwait(false); semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false); // Get the annotated reference nodes. nonConflictingIdentifierNodes = await FindReferenceAnnotatedNodesAsync(updatedDocument, cancellationToken).ConfigureAwait(false); var topmostParentingExpressions = nonConflictingIdentifierNodes .Select(ident => GetTopMostParentingExpression(ident)) .Distinct().ToList(); var originalInitializerSymbolInfo = semanticModel.GetSymbolInfo(variableDeclarator.Initializer.Value, cancellationToken); // Checks to see if inlining the temporary variable may change the code's meaning. This can only apply if the variable has two or more // references. We later use this heuristic to determine whether or not to display a warning message to the user. var mayContainSideEffects = references.Count() > 1 && MayContainSideEffects(variableDeclarator.Initializer.Value); // Make each topmost parenting statement or Equals Clause Expressions semantically explicit. updatedDocument = await updatedDocument.ReplaceNodesAsync(topmostParentingExpressions, (o, n) => { var node = Simplifier.Expand(n, semanticModel, workspace, cancellationToken: cancellationToken); // warn when inlining into a conditional expression, as the inlined expression will not be executed. if (semanticModel.GetSymbolInfo(o).Symbol is IMethodSymbol { IsConditional : true }) { node = node.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_into_conditional_method_call)); } // If the refactoring may potentially change the code's semantics, display a warning message to the user. if (mayContainSideEffects) { node = node.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_variable_may_change_code_meaning)); } return(node); }, cancellationToken).ConfigureAwait(false);
/// 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); } }