public ITriviaSavedResult SaveTriviaAroundSelection(SyntaxNode root, TextSpan textSpan) { Contract.ThrowIfNull(root); Contract.ThrowIfTrue(textSpan.IsEmpty); Debug.Assert(Enum.GetNames(typeof(TriviaLocation)).Length == TriviaLocationsCount); var tokens = GetTokensAtEdges(root, textSpan); // span must contain after and before spans at the both edges Contract.ThrowIfFalse( textSpan.Contains(tokens[TriviaLocation.AfterBeginningOfSpan].Span) && textSpan.Contains(tokens[TriviaLocation.BeforeEndOfSpan].Span) ); var triviaList = GetTriviaAtEdges(tokens, textSpan); var annotations = Enumerable .Range((int)TriviaLocation.BeforeBeginningOfSpan, TriviaLocationsCount) .Cast <TriviaLocation>() .ToDictionary(location => location, _ => new SyntaxAnnotation()); var map = CreateOldToNewTokensMap(tokens, annotations); var rootWithAnnotation = ReplaceTokens(root, map.Keys, (o, n) => map[o]); return(CreateResult(rootWithAnnotation, annotations, triviaList)); }
public void TextSpanContains00() { TextSpan span = new TextSpan(0, 10); Assert.True(span.Contains(3)); Assert.False(span.Contains(30)); Assert.False(span.Contains(11)); Assert.False(span.Contains(-1)); }
/// <summary> /// Find all regions in a file and get there start and end line /// </summary> /// <param name="tree">SyntaxTree for the given file</param> /// <param name="span">Start and end line in which we search for regions</param> /// <returns>Flat list of regions</returns> public static List <CodeRegionItem> MapRegions(SyntaxTree tree, TextSpan span, ICodeViewUserControl control) { var regionList = new List <CodeRegionItem>(); if (tree == null) { return(regionList); } if (SettingsHelper.FilterRules != null) { var filterRule = SettingsHelper.FilterRules.LastOrDefault(f => f.Kind == CodeItemKindEnum.Region || f.Kind == CodeItemKindEnum.All); if (filterRule != null && filterRule.Ignore) { return(regionList); } } var root = tree.GetRoot(); // Find all start points of regions foreach (var regionDirective in root.DescendantTrivia() .Where(i => (i.RawKind == (int)SyntaxKind.RegionDirectiveTrivia || i.RawKind == (int)VisualBasic.SyntaxKind.RegionDirectiveTrivia) && span.Contains(i.Span))) { regionList.Add(MapRegion(regionDirective, control)); } if (!regionList.Any()) { return(regionList); } // Find all matching end points of regions foreach (var endRegionDirective in root.DescendantTrivia() .Where(j => (j.RawKind == (int)SyntaxKind.EndRegionDirectiveTrivia || j.RawKind == (int)VisualBasic.SyntaxKind.EndRegionDirectiveTrivia) && span.Contains(j.Span))) { var reg = regionList.LastOrDefault(x => x.StartLine < GetStartLine(endRegionDirective) && x.EndLine == 0); if (reg != null) { reg.EndLine = GetEndLine(endRegionDirective); reg.EndLinePosition = GetEndLinePosition(endRegionDirective); } } var list = ToHierarchy(regionList, int.MinValue, int.MaxValue); return(list.Select(r => r as CodeRegionItem).ToList()); }
public static async Task ComputeRefactoringAsync(RefactoringContext context, ClassDeclarationSyntax classDeclaration) { SyntaxToken identifier = classDeclaration.Identifier; if (identifier.IsMissing) { return; } TextSpan span = identifier.Span; BaseListSyntax baseList = classDeclaration.BaseList; if (baseList != null) { span = TextSpan.FromBounds(span.Start, baseList.Span.End); } TypeParameterListSyntax typeParameterList = classDeclaration.TypeParameterList; if (typeParameterList != null) { span = TextSpan.FromBounds(span.Start, typeParameterList.Span.End); } if (!span.Contains(context.Span)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration, context.CancellationToken); if (classSymbol?.IsErrorType() != false) { return; } if (classSymbol.IsStatic) { return; } foreach (INamedTypeSymbol interfaceSymbol in classSymbol.AllInterfaces) { if (interfaceSymbol.HasMetadataName(MetadataNames.System_IEquatable_T) && interfaceSymbol.TypeArguments.Single().Equals(classSymbol)) { return; } } INamedTypeSymbol equatableSymbol = semanticModel.GetTypeByMetadataName("System.IEquatable`1").Construct(classSymbol); context.RegisterRefactoring( GetTitle(equatableSymbol, semanticModel, classDeclaration.SpanStart), f => RefactorAsync(context.Document, classDeclaration, classSymbol, equatableSymbol, semanticModel, f), RefactoringIdentifiers.ImplementIEquatableOfT); }
private static bool SpanInvolvesLocalFunction(TextSpan finalSpan, SemanticModel model, SyntaxNode root) { var nodes = root.DescendantNodes(finalSpan).Where(n => finalSpan.Contains(n.Span)); foreach (var node in nodes) { if (node.IsKind(SyntaxKind.LocalFunctionStatement)) { return(true); } if (node is IdentifierNameSyntax id) { var symbolInfo = model.GetSymbolInfo(id); if (symbolInfo.Symbol is IMethodSymbol method && method.MethodKind == MethodKind.LocalFunction) { return(true); } } if (node.HasAncestor <LocalFunctionStatementSyntax>()) { return(true); } } return(false); }
public static bool IsApplicableSpan(InitializerExpressionSyntax initializer, TextSpan span) { SeparatedSyntaxList <ExpressionSyntax> expressions = initializer.Expressions; if (!expressions.Any()) { return(true); } if (span.IsEmpty) { if (expressions.Count == expressions.SeparatorCount && TextSpan.FromBounds(expressions.GetSeparator(expressions.Count - 1).Span.End, initializer.CloseBraceToken.SpanStart).Contains(span)) { return(true); } TextSpan span2 = TextSpan.FromBounds(expressions.Last().Span.End, initializer.CloseBraceToken.SpanStart); if (span2.Length > 0) { span2 = new TextSpan(span2.Start + 1, span2.Length - 1); if (span2.Contains(span)) { return(true); } } } return(false); }
private bool GetOuterMostTupleExpressionInSpan( SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, TextSpan currentSpan, CancellationToken cancellationToken, [NotNullWhen(true)] out TupleExpressionSyntax?result ) { result = null; while ( TryGetTupleExpression( SignatureHelpTriggerReason.InvokeSignatureHelpCommand, root, position, syntaxFacts, cancellationToken, out var expression ) ) { if (!currentSpan.Contains(expression.Span)) { break; } result = expression; position = expression.SpanStart; } return(result != null); }
public static bool ContainArgumentlessThrowWithoutEnclosingCatch( this IEnumerable <SyntaxToken> tokens, TextSpan textSpan ) { foreach (var token in tokens) { if (token.Kind() != SyntaxKind.ThrowKeyword) { continue; } if ( !(token.Parent is ThrowStatementSyntax throwStatement) || throwStatement.Expression != null ) { continue; } var catchClause = token.GetAncestor <CatchClauseSyntax>(); if (catchClause == null || !textSpan.Contains(catchClause.Span)) { return(true); } } return(false); }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, ParameterSyntax parameter) { if (!context.IsAnyRefactoringEnabled( RefactoringIdentifiers.AddIdentifierToParameter, RefactoringIdentifiers.RenameParameterAccordingToTypeName)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); IParameterSymbol parameterSymbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); if (parameterSymbol?.Type == null) { return; } if (parameter.Identifier.IsMissing) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.AddIdentifierToParameter)) { TextSpan span = (parameter.Type != null) ? TextSpan.FromBounds(parameter.Type.Span.End, parameter.Span.End) : parameter.Span; if (span.Contains(context.Span)) { string name = NameGenerator.CreateName(parameterSymbol.Type, firstCharToLower: true); if (!string.IsNullOrEmpty(name)) { context.RegisterRefactoring( $"Add identifier '{name}'", cancellationToken => AddParameterNameToParameterAsync(context.Document, parameter, name, cancellationToken), RefactoringIdentifiers.AddIdentifierToParameter); } } } } else if (context.IsRefactoringEnabled(RefactoringIdentifiers.RenameParameterAccordingToTypeName) && parameter.Identifier.Span.Contains(context.Span)) { string oldName = parameter.Identifier.ValueText; string newName = NameGenerator.Default.CreateUniqueParameterName( oldName, parameterSymbol, semanticModel, cancellationToken: context.CancellationToken); if (newName != null) { context.RegisterRefactoring( $"Rename '{oldName}' to '{newName}'", cancellationToken => Renamer.RenameSymbolAsync(context.Solution, parameterSymbol, newName, default(OptionSet), cancellationToken), RefactoringIdentifiers.RenameParameterAccordingToTypeName); } } }
private static List <MemberDeclarationSyntax> GetMembersInSpan( TextSpan textSpan, TypeDeclarationSyntax containingType, MemberDeclarationSyntax firstMember) { List <MemberDeclarationSyntax> selectedMembers = null; var members = containingType.Members; var fieldIndex = members.IndexOf(firstMember); if (fieldIndex < 0) { return(null); } for (var i = fieldIndex; i < members.Count; i++) { var member = members[i]; if (textSpan.Contains(member.Span)) { selectedMembers = selectedMembers ?? new List <MemberDeclarationSyntax>(); selectedMembers.Add(member); } else if (textSpan.OverlapsWith(member.Span)) { return(null); } else { break; } } return(selectedMembers); }
private static bool CheckTrivia(ElseClauseSyntax elseClause, IfStatementSyntax ifStatement) { TextSpan elseSpan = elseClause.Span; TextSpan ifSpan = ifStatement.Span; TextSpan span = TextSpan.FromBounds(elseSpan.Start, ifSpan.Start); TextSpan span2 = TextSpan.FromBounds(ifSpan.End, elseSpan.End); foreach (SyntaxTrivia trivia in elseClause.DescendantTrivia()) { TextSpan triviaSpan = trivia.Span; if (span.Contains(triviaSpan)) { if (!trivia.IsWhitespaceOrEndOfLineTrivia()) { return(false); } } else if (span2.Contains(triviaSpan)) { if (!trivia.IsWhitespaceOrEndOfLineTrivia()) { return(false); } } } return(true); }
public static IEnumerable <TSyntaxNode> Traverse <TSyntaxNode>( this SyntaxNode node, TextSpan searchSpan, Func <SyntaxNode, bool> predicate) where TSyntaxNode : SyntaxNode { Contract.ThrowIfNull(node); var nodes = new LinkedList <SyntaxNode>(); nodes.AddFirst(node); while (nodes.Count > 0) { var currentNode = nodes.First.Value; nodes.RemoveFirst(); if (currentNode != null && searchSpan.Contains(currentNode.FullSpan) && predicate(currentNode)) { if (currentNode is TSyntaxNode tSyntax) { yield return(tSyntax); } nodes.AddRangeAtHead(currentNode.ChildNodes()); } } }
private ImmutableArray <DiagnosticData> AdjustInitialDiagnostics( Solution solution, UpdatedEventArgs args, CancellationToken cancellationToken) { // we only reach here if there is the document var document = solution.GetDocument(args.DocumentId); // if there is no source text for this document, we don't populate the initial tags. this behavior is equivalent of existing // behavior in OnDiagnosticsUpdated. if (!document.TryGetText(out var text)) { return(ImmutableArray <DiagnosticData> .Empty); } // GetDiagnostics returns whatever cached diagnostics in the service which can be stale ones. for example, build error will be most likely stale // diagnostics. so here we make sure we filter out any diagnostics that is not in the text range. var builder = ArrayBuilder <DiagnosticData> .GetInstance(); var fullSpan = new TextSpan(0, text.Length); foreach (var diagnostic in diagService.GetDiagnostics( args.Workspace, args.ProjectId, args.DocumentId, args.Id, includeSuppressedDiagnostics: false, cancellationToken: cancellationToken)) { if (fullSpan.Contains(diagnostic.GetExistingOrCalculatedTextSpan(text))) { builder.Add(diagnostic); } } return(builder.ToImmutableAndFree()); }
static public (IXor2ComputationNodeReference, AlreadyProcessedSyntaxNodes) FromReturnStatement(ReturnStatementSyntax returnSyntax, MethodBlockAnalysis methodAnalysis, TextSpan span, AlreadyProcessedSyntaxNodes processedNodesOrNull = null) { Throw.IfNot(methodAnalysis.DataFlowAnalysis.Succeeded); var processedNodes = (processedNodesOrNull ?? new AlreadyProcessedSyntaxNodes()); if (!span.Contains(returnSyntax.Span)) { return(_CreatePair(Xor2ComputationNodeReference.OutOfScope(returnSyntax), processedNodes)); } var rhsSyntax = SyntaxOperations.GetRightHandSideExpression(returnSyntax); var previous = _DispatchXor7ExpressionSyntax(rhsSyntax, methodAnalysis, processedNodes ?? new AlreadyProcessedSyntaxNodes(), span); var node = ComputationGraphNode.FromReturnStatement(returnSyntax, previous.Item1); if (!previous.Item1.IsA) { return(_CreatePair(node, previous.Item2)); } return(_CreatePair(node, previous.Item2)); }
public static async Task ComputeRefactoringAsync(RefactoringContext context, StructDeclarationSyntax structDeclaration) { SyntaxToken identifier = structDeclaration.Identifier; if (identifier.IsMissing) { return; } TextSpan span = identifier.Span; BaseListSyntax baseList = structDeclaration.BaseList; if (baseList != null) { span = TextSpan.FromBounds(span.Start, baseList.Span.End); } TypeParameterListSyntax typeParameterList = structDeclaration.TypeParameterList; if (typeParameterList != null) { span = TextSpan.FromBounds(span.Start, typeParameterList.Span.End); } if (!span.Contains(context.Span)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol typeSymbol = semanticModel.GetDeclaredSymbol(structDeclaration, context.CancellationToken); if (typeSymbol?.IsErrorType() != false) { return; } INamedTypeSymbol equatableSymbol = semanticModel.GetTypeByMetadataName(MetadataNames.System_IEquatable_T); if (equatableSymbol == null) { return; } equatableSymbol = equatableSymbol.Construct(typeSymbol); if (typeSymbol.Implements(equatableSymbol, allInterfaces: true)) { return; } context.RegisterRefactoring( GetTitle(equatableSymbol, semanticModel, structDeclaration.SpanStart), f => RefactorAsync(context.Document, structDeclaration, typeSymbol, equatableSymbol, semanticModel, f), RefactoringIdentifiers.ImplementIEquatableOfT); }
/// <summary> /// <para> /// Determines if a <paramref name="node"/> is underselected given <paramref name="selection"/>. /// </para> /// <para> /// Underselection is defined as omitting whole nodes from either the beginning or the end. It can be used e.g. to detect that /// following selection `1 + [|2 + 3|]` is underselecting the whole expression node tree. /// </para> /// <para> /// Returns false if only and precisely one <see cref="SyntaxToken"/> is selected. In that case the <paramref name="selection"/> /// is treated more as a caret location. /// </para> /// <para> /// It's intended to be used in conjunction with <see cref="IRefactoringHelpersService.GetRelevantNodesAsync{TSyntaxNode}(Document, TextSpan, CancellationToken)"/> /// that, for non-empty selections, returns the smallest encompassing node. A node that can, for certain refactorings, be too large given user-selection even though /// it is the smallest that can be retrieved. /// </para> /// <para> /// When <paramref name="selection"/> doesn't intersect the node in any way it's not considered to be underselected. /// </para> /// <para> /// Null node is always considered underselected. /// </para> /// </summary> public static bool IsNodeUnderselected(SyntaxNode?node, TextSpan selection) { // Selection is null -> it's always underselected // REASON: Easier API use -> underselected node, don't work on it further if (node == null) { return(true); } // Selection or node is empty -> can't be underselected if (selection.IsEmpty || node.Span.IsEmpty) { return(false); } // Selection is larger than node.Span -> can't be underselecting if (selection.Contains(node.Span)) { return(false); } // Selection doesn't intersect node -> can't be underselecting. // RATIONALE: If there's no intersection then we got the node in some other way, e.g. // extracting it after user selected `;` at the end of an expression statement // `foo()[|;|]` for `foo()` node. if (!node.FullSpan.OverlapsWith(selection)) { return(false); } // Only precisely one token of the node is selected -> treat is as empty selection -> not // underselected. The rationale is that if only one Token is selected then the selection // wasn't about precisely getting the one node and nothing else & therefore we should treat // it as empty selection. if (node.FullSpan.Contains(selection.Start)) { var selectionStartToken = node.FindToken(selection.Start); if (selection.IsAround(selectionStartToken)) { return(false); } } var beginningNode = node.FindToken(node.Span.Start).Parent; var endNode = node.FindToken(node.Span.End - 1).Parent; RoslynDebug.Assert(beginningNode is object); RoslynDebug.Assert(endNode is object); // Node is underselected if either the first (lowest) child doesn't contain start of selection // of the last child doesn't intersect with the end. // Node is underselected if either the first (lowest) child ends before the selection has started // or the last child starts after the selection ends (i.e. one of them is completely on the outside of selection). // It's a crude heuristic but it allows omitting parts of nodes or trivial tokens from the beginning/end // but fires up e.g.: `1 + [|2 + 3|]`. return(beginningNode.Span.End <= selection.Start || endNode.Span.Start >= selection.End); }
public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia) { if (_textSpan.Contains(trivia.Span)) { return(SyntaxFactory.Whitespace(trivia.ToString().Replace("\t", " "))); } return(base.VisitTrivia(trivia)); }
internal static void ApplyNewTree(TextEditor editor, int startOffset, bool exact, TextSpan span, Microsoft.CodeAnalysis.SyntaxTree syntaxTree, Microsoft.CodeAnalysis.SyntaxTree newTree) { var caretOffset = editor.CaretOffset; var caretEndOffset = caretOffset; using (var undo = editor.OpenUndoGroup()) { int delta = 0; foreach (var change in newTree.GetChanges(syntaxTree)) { if (!exact && change.Span.Start >= caretOffset) { continue; } if (exact && !span.Contains(change.Span.Start)) { continue; } var newText = change.NewText; var length = change.Span.Length; var changeEnd = delta + change.Span.End - 1; if (changeEnd < editor.Length && changeEnd >= 0 && editor.GetCharAt(changeEnd) == '\r') { length--; } var replaceOffset = delta + change.Span.Start; editor.ReplaceText(replaceOffset, length, newText); delta = delta - length + newText.Length; if (change.Span.Start < caretOffset) { if (change.Span.End < caretOffset) { caretEndOffset += newText.Length - length; } else { caretEndOffset = replaceOffset; } } } } if (startOffset < caretOffset) { if (0 <= caretEndOffset && caretEndOffset < editor.Length) { editor.CaretOffset = caretEndOffset; } if (editor.CaretColumn == 1) { if (editor.CaretLine > 1 && editor.GetLine(editor.CaretLine - 1).Length == 0) { editor.CaretLine--; } editor.CaretColumn = editor.GetVirtualIndentationColumn(editor.CaretLine); } } }
public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia) { if (_textSpan.Contains(trivia.Span)) { return(SyntaxFactory.Whitespace(string.Empty)); } return(base.VisitTrivia(trivia)); }
private static bool ContainsInterleavedDirective( TextSpan textSpan, SyntaxTrivia trivia, CancellationToken cancellationToken) { if (trivia.HasStructure) { var structure = trivia.GetStructure(); var parentSpan = structure.Span; if (trivia.GetStructure().IsKind(SyntaxKind.RegionDirectiveTrivia, SyntaxKind.EndRegionDirectiveTrivia, SyntaxKind.IfDirectiveTrivia, SyntaxKind.EndIfDirectiveTrivia)) { var match = ((DirectiveTriviaSyntax)structure).GetMatchingDirective(cancellationToken); if (match != null) { var matchSpan = match.Span; if (!textSpan.Contains(matchSpan.Start)) { // The match for this pp directive is outside // this node. return(true); } } } else if (trivia.GetStructure().IsKind(SyntaxKind.ElseDirectiveTrivia, SyntaxKind.ElifDirectiveTrivia)) { var directives = ((DirectiveTriviaSyntax)structure).GetMatchingConditionalDirectives(cancellationToken); if (directives != null && directives.Count > 0) { if (!textSpan.Contains(directives[0].SpanStart) || !textSpan.Contains(directives[directives.Count - 1].SpanStart)) { // This else/elif belongs to a pp span that isn't // entirely within this node. return(true); } } } } return(false); }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, ParameterSyntax parameter) { if (!context.IsAnyRefactoringEnabled( RefactoringIdentifiers.AddParameterNameToParameter, RefactoringIdentifiers.RenameParameterAccordingToTypeName)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); IParameterSymbol parameterSymbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); if (parameterSymbol?.Type == null) { return; } if (parameter.Identifier.IsMissing) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.AddParameterNameToParameter)) { TextSpan span = (parameter.Type != null) ? TextSpan.FromBounds(parameter.Type.Span.End, parameter.Span.End) : parameter.Span; if (span.Contains(context.Span)) { string name = SyntaxUtility.CreateIdentifier(parameterSymbol.Type, firstCharToLower: true); if (!string.IsNullOrEmpty(name)) { context.RegisterRefactoring( $"Add parameter name '{name}'", cancellationToken => AddParameterNameToParameterAsync(context.Document, parameter, name, cancellationToken)); } } } } else if (context.IsRefactoringEnabled(RefactoringIdentifiers.RenameParameterAccordingToTypeName) && parameter.Identifier.Span.Contains(context.Span)) { string name = parameter.Identifier.ValueText; string newName = SyntaxUtility.CreateIdentifier(parameterSymbol.Type, firstCharToLower: true); if (!string.IsNullOrEmpty(newName) && !string.Equals(name, newName, StringComparison.Ordinal)) { ISymbol symbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); context.RegisterRefactoring( $"Rename parameter to '{newName}'", cancellationToken => SymbolRenamer.RenameAsync(context.Document, symbol, newName, cancellationToken)); } } }
public static Task <Document> RefactorAsync( Document document, MemberDeclarationSyntax declaration, TextSpan span, CancellationToken cancellationToken) { MemberDeclarationSyntax newDeclaration = declaration; ImmutableArray <string> comments; SyntaxTriviaList leadingTrivia = declaration.GetLeadingTrivia(); if (leadingTrivia.Span.Contains(span)) { comments = leadingTrivia .Where(f => span.Contains(f.Span) && f.Kind() == SyntaxKind.SingleLineCommentTrivia) .Select(f => _leadingSlashesRegex.Replace(f.ToString(), "")) .ToImmutableArray(); TextSpan spanToRemove = TextSpan.FromBounds(span.Start, declaration.SpanStart); newDeclaration = declaration.WithLeadingTrivia(leadingTrivia.Where(f => !spanToRemove.Contains(f.Span))); } else { SyntaxTrivia trivia = declaration.FindTrivia(span.Start); Debug.Assert(trivia != default(SyntaxTrivia)); SyntaxToken token = trivia.Token; SyntaxTriviaList trailingTrivia = token.TrailingTrivia; Debug.Assert(trailingTrivia.Contains(trivia)); for (int i = 0; i < trailingTrivia.Count; i++) { if (trailingTrivia[i].Span == span) { comments = ImmutableArray.Create(_leadingSlashesRegex.Replace(trailingTrivia[i].ToString(), "")); SyntaxToken newToken = token.WithTrailingTrivia(trailingTrivia.Skip(i + 1)); newDeclaration = newDeclaration.ReplaceToken(token, newToken); break; } } } var settings = new DocumentationCommentGeneratorSettings(comments); newDeclaration = newDeclaration.WithNewSingleLineDocumentationComment(settings); return(document.ReplaceNodeAsync(declaration, newDeclaration, cancellationToken)); }
public static bool ContainPreprocessorCrossOver( this IEnumerable <SyntaxToken> tokens, TextSpan textSpan ) { var activeRegions = 0; var activeIfs = 0; foreach (var trivia in tokens.GetAllTrivia()) { if (!textSpan.Contains(trivia.Span)) { continue; } switch (trivia.Kind()) { case SyntaxKind.RegionDirectiveTrivia: activeRegions++; break; case SyntaxKind.EndRegionDirectiveTrivia: if (activeRegions <= 0) { return(true); } activeRegions--; break; case SyntaxKind.IfDirectiveTrivia: activeIfs++; break; case SyntaxKind.EndIfDirectiveTrivia: if (activeIfs <= 0) { return(true); } activeIfs--; break; case SyntaxKind.ElseDirectiveTrivia: case SyntaxKind.ElifDirectiveTrivia: if (activeIfs <= 0) { return(true); } break; } } return(activeIfs != 0 || activeRegions != 0); }
/// <summary> /// Find all regions in a file and get there start and end line /// </summary> /// <param name="tree">SyntaxTree for the given file</param> /// <param name="span">Start and end line in which we search for regions</param> /// <returns>Flat list of regions</returns> public static List <CodeRegionItem> MapRegions(SyntaxTree tree, TextSpan span) { var regionList = new List <CodeRegionItem>(); if (tree == null) { return(regionList); } var root = tree.GetRoot(); // Find all start points of regions foreach (var regionDirective in root.DescendantTrivia() .Where(i => (i.RawKind == (int)SyntaxKind.RegionDirectiveTrivia || i.RawKind == (int)VisualBasic.SyntaxKind.RegionDirectiveTrivia) && span.Contains(i.Span))) { regionList.Add(MapRegion(regionDirective)); } if (!regionList.Any()) { return(regionList); } // Find all matching end points of regions foreach (var endRegionDirective in root.DescendantTrivia() .Where(j => (j.RawKind == (int)SyntaxKind.EndRegionDirectiveTrivia || j.RawKind == (int)VisualBasic.SyntaxKind.EndRegionDirectiveTrivia) && span.Contains(j.Span))) { var reg = regionList.LastOrDefault(x => x.StartLine < GetStartLine(endRegionDirective) && x.EndLine == 0); if (reg != null) { reg.EndLine = GetEndLine(endRegionDirective); } } var list = ToHierarchy(regionList, int.MinValue, int.MaxValue); return(list.Select(r => r as CodeRegionItem).ToList()); }
private static bool IsEmptyAndContainedInSpanOrBetweenSpans(this TextSpan span, TextSpan innerSpan, TextSpan outerSpan) { if (span.IsEmpty) { return(innerSpan.Contains(span)); } else { return(span.IsBetweenSpans(innerSpan, outerSpan)); } }
public static bool IsContainedInSpanOrBetweenSpans(this TextSpan span, SyntaxNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } TextSpan innerSpan = node.Span; return(innerSpan.Contains(span) || span.IsBetweenSpans(innerSpan, node.FullSpan)); }
public static void Refactor( CodeRefactoringContext context, ParameterSyntax parameter, SemanticModel semanticModel) { if (parameter == null) { throw new ArgumentNullException(nameof(parameter)); } if (semanticModel == null) { throw new ArgumentNullException(nameof(semanticModel)); } IParameterSymbol parameterSymbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); if (parameterSymbol?.Type == null) { return; } if (parameter.Identifier.IsMissing) { TextSpan span = (parameter.Type != null) ? TextSpan.FromBounds(parameter.Type.Span.End, parameter.Span.End) : parameter.Span; if (span.Contains(context.Span)) { string name = CreateParameterName(parameterSymbol.Type, semanticModel); if (name != null) { context.RegisterRefactoring( $"Add parameter name '{name}'", cancellationToken => AddParameterNameAccordingToTypeNameRefactorAsync(context.Document, parameter, name, cancellationToken)); } } } else if (parameter.Identifier.Span.Contains(context.Span)) { string name = CreateParameterName(parameterSymbol.Type, semanticModel); if (name != null) { ISymbol symbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); context.RegisterRefactoring( $"Rename parameter to '{name}'", cancellationToken => symbol.RenameAsync(name, context.Document, cancellationToken)); } } }
private static IEnumerable <IdentifierNameSyntax> DescendantIdentifierNames(SyntaxNode node, TextSpan excludedSpan) { foreach (SyntaxNode descendant in node.DescendantNodes(node.Span)) { if (descendant.IsKind(SyntaxKind.IdentifierName) && !excludedSpan.Contains(descendant.Span)) { yield return((IdentifierNameSyntax)descendant); } } }
public void TextSpanContains01() { TextSpan span_05_15 = new TextSpan(5, 10); TextSpan span_03_10 = new TextSpan(3, 7); TextSpan span_10_11 = new TextSpan(10, 1); TextSpan span_00_03 = new TextSpan(0, 3); // non-overlapping Assert.False(span_05_15.Contains(span_00_03)); Assert.False(span_00_03.Contains(span_05_15)); // overlap with slop Assert.True(span_05_15.Contains(span_10_11)); // same span Assert.True(span_05_15.Contains(span_05_15)); // partial overlap Assert.False(span_05_15.Contains(span_03_10)); Assert.False(span_03_10.Contains(span_05_15)); }
public ITriviaSavedResult SaveTriviaAroundSelection(SyntaxNode root, TextSpan textSpan) { Contract.ThrowIfNull(root); Contract.ThrowIfTrue(textSpan.IsEmpty); Contract.Requires(Enum.GetNames(typeof(TriviaLocation)).Length == TriviaLocationsCount); var tokens = GetTokensAtEdges(root, textSpan); // span must contain after and before spans at the both edges Contract.ThrowIfFalse(textSpan.Contains(tokens[TriviaLocation.AfterBeginningOfSpan].Span) && textSpan.Contains(tokens[TriviaLocation.BeforeEndOfSpan].Span)); var triviaList = GetTriviaAtEdges(tokens, textSpan); var annotations = Enumerable.Range((int)TriviaLocation.BeforeBeginningOfSpan, TriviaLocationsCount) .Cast<TriviaLocation>() .ToDictionary(location => location, _ => new SyntaxAnnotation()); var map = CreateOldToNewTokensMap(tokens, annotations); var rootWithAnnotation = ReplaceTokens(root, map.Keys, (o, n) => map[o]); return CreateResult(rootWithAnnotation, annotations, triviaList); }
private static bool ContainsInterleavedDirective( TextSpan textSpan, SyntaxTrivia trivia, ref SimpleIntervalTree <TextSpan> ifEndIfSpans, CancellationToken cancellationToken) { if (trivia.HasStructure) { var parentSpan = trivia.GetStructure().Span; if (trivia.GetStructure().IsKind(SyntaxKind.RegionDirectiveTrivia) || trivia.GetStructure().IsKind(SyntaxKind.EndRegionDirectiveTrivia) || trivia.GetStructure().IsKind(SyntaxKind.IfDirectiveTrivia) || trivia.GetStructure().IsKind(SyntaxKind.EndIfDirectiveTrivia)) { var match = ((DirectiveTriviaSyntax)trivia.GetStructure()).GetMatchingDirective(cancellationToken); if (match != null) { var matchSpan = match.Span; if (!textSpan.Contains(matchSpan.Start)) { // The match for this pp directive is outside // this node. return(true); } if (trivia.GetStructure().IsKind(SyntaxKind.IfDirectiveTrivia) || trivia.GetStructure().IsKind(SyntaxKind.EndIfDirectiveTrivia)) { var ppSpan = TextSpan.FromBounds( Math.Min(parentSpan.Start, matchSpan.Start), Math.Max(parentSpan.End, matchSpan.End)); ifEndIfSpans = ifEndIfSpans.AddInterval(ppSpan); } } } else if ( trivia.GetStructure().IsKind(SyntaxKind.ElseDirectiveTrivia) || trivia.GetStructure().IsKind(SyntaxKind.ElifDirectiveTrivia)) { if (!ifEndIfSpans.IntersectsWith(parentSpan.Start)) { // This else/elif belongs to a pp span that isn't // entirely within this node. return(true); } } } return(false); }
private bool GetOuterMostParenthesizedExpressionInSpan(SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, TextSpan currentSpan, CancellationToken cancellationToken, out ParenthesizedExpressionSyntax result) { result = null; while (TryGetParenthesizedExpression(SignatureHelpTriggerReason.InvokeSignatureHelpCommand, root, position, syntaxFacts, cancellationToken, out var expression)) { if (!currentSpan.Contains(expression.Span)) { break; } result = expression; position = expression.SpanStart; } return result != null; }
private static List<MemberDeclarationSyntax> GetMembersInSpan( TextSpan textSpan, TypeDeclarationSyntax containingType, MemberDeclarationSyntax firstMember) { List<MemberDeclarationSyntax> selectedMembers = null; var members = containingType.Members; var fieldIndex = members.IndexOf(firstMember); if (fieldIndex < 0) { return null; } for (var i = fieldIndex; i < members.Count; i++) { var member = members[i]; if (textSpan.Contains(member.Span)) { selectedMembers = selectedMembers ?? new List<MemberDeclarationSyntax>(); selectedMembers.Add(member); } else if (textSpan.OverlapsWith(member.Span)) { return null; } else { break; } } return selectedMembers; }
private void AdjustIndentationForSpan( Document document, ITextEdit edit, TextSpan visibleSpan, IFormattingRule baseIndentationRule, OptionSet options) { var root = document.GetSyntaxRootAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None); using (var rulePool = SharedPools.Default<List<IFormattingRule>>().GetPooledObject()) using (var spanPool = SharedPools.Default<List<TextSpan>>().GetPooledObject()) { var venusFormattingRules = rulePool.Object; var visibleSpans = spanPool.Object; venusFormattingRules.Add(baseIndentationRule); venusFormattingRules.Add(ContainedDocumentPreserveFormattingRule.Instance); var formattingRules = venusFormattingRules.Concat(Formatter.GetDefaultFormattingRules(document)); var workspace = document.Project.Solution.Workspace; var changes = Formatter.GetFormattedTextChanges( root, new TextSpan[] { CommonFormattingHelpers.GetFormattingSpan(root, visibleSpan) }, workspace, options, formattingRules, CancellationToken.None); visibleSpans.Add(visibleSpan); var newChanges = FilterTextChanges(document.GetTextAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None), visibleSpans, changes.ToReadOnlyCollection()).Where(t => visibleSpan.Contains(t.Span)); foreach (var change in newChanges) { edit.Replace(change.Span.ToSpan(), change.NewText); } } }
/// <summary> /// Finds the member in the containing symbol which is inside the given declaration span. /// </summary> private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan declarationSpan, string name = null) { if ((object)container == null) { return null; } // look for any member with same declaration location var collection = name != null ? container.GetMembers(name) : container.GetMembersUnordered(); Symbol zeroWidthMatch = null; foreach (var symbol in collection) { var namedType = symbol as ImplicitNamedTypeSymbol; if ((object)namedType != null && namedType.IsImplicitClass) { // look inside wrapper around illegally placed members in namespaces var result = GetDeclaredMember(namedType, declarationSpan, name); if ((object)result != null) { return result; } } foreach (var loc in symbol.Locations) { if (loc.IsInSource && loc.SourceTree == this.SyntaxTree && declarationSpan.Contains(loc.SourceSpan)) { if (loc.SourceSpan.IsEmpty && loc.SourceSpan.End == declarationSpan.Start) { // exclude decls created via syntax recovery zeroWidthMatch = symbol; } else { return symbol; } } } // Handle the case of the implementation of a partial method. var partial = symbol.Kind == SymbolKind.Method ? ((MethodSymbol)symbol).PartialImplementationPart : null; if ((object)partial != null) { var loc = partial.Locations[0]; if (loc.IsInSource && loc.SourceTree == this.SyntaxTree && declarationSpan.Contains(loc.SourceSpan)) { return partial; } } } // If we didn't find anything better than the symbol that matched because of syntax error recovery, then return that. // Otherwise, if there's a name, try again without a name. // Otherwise, give up. return zeroWidthMatch ?? (name != null ? GetDeclaredMember(container, declarationSpan) : null); }
public void TextSpan_ContainsItself() { var textSpan = new TextSpan(SourceText.From(""), 1, 2); Assert.True(textSpan.Contains(textSpan)); }
public void TextSpan_DoesNotContainEnd() { var textSpan = new TextSpan(SourceText.From(""), 1, 2); Assert.False(textSpan.Contains(textSpan.End)); }
public void TextSpan_ContainsStart() { var textSpan = new TextSpan(null, 1, 2); Assert.True(textSpan.Contains(textSpan.Start)); }
private async Task<SuppressionTargetInfo> GetSuppressionTargetInfoAsync(Document document, TextSpan span, CancellationToken cancellationToken) { var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (syntaxTree.GetLineVisibility(span.Start, cancellationToken) == LineVisibility.Hidden) { return null; } // Find the start token to attach leading pragma disable warning directive. var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var lines = syntaxTree.GetText(cancellationToken).Lines; var indexOfLine = lines.IndexOf(span.Start); var lineAtPos = lines[indexOfLine]; var startToken = root.FindToken(lineAtPos.Start); startToken = GetAdjustedTokenForPragmaDisable(startToken, root, lines, indexOfLine); // Find the end token to attach pragma restore warning directive. var spanEnd = Math.Max(startToken.Span.End, span.End); indexOfLine = lines.IndexOf(spanEnd); lineAtPos = lines[indexOfLine]; var endToken = root.FindToken(lineAtPos.End); endToken = GetAdjustedTokenForPragmaRestore(endToken, root, lines, indexOfLine); var nodeWithTokens = GetNodeWithTokens(startToken, endToken, root); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>(); ISymbol targetSymbol = null; var targetMemberNode = syntaxFacts.GetContainingMemberDeclaration(root, startToken.SpanStart); if (targetMemberNode != null) { targetSymbol = semanticModel.GetDeclaredSymbol(targetMemberNode, cancellationToken); if (targetSymbol == null) { var analyzerDriverService = document.GetLanguageService<IAnalyzerDriverService>(); // targetMemberNode could be a declaration node with multiple decls (e.g. field declaration defining multiple variables). // Let us compute all the declarations intersecting the span. var decls = new List<DeclarationInfo>(); analyzerDriverService.ComputeDeclarationsInSpan(semanticModel, span, true, decls, cancellationToken); if (decls.Any()) { var containedDecls = decls.Where(d => span.Contains(d.DeclaredNode.Span)); if (containedDecls.Count() == 1) { // Single containing declaration, use this symbol. var decl = containedDecls.Single(); targetSymbol = decl.DeclaredSymbol; } else { // Otherwise, use the most enclosing declaration. TextSpan? minContainingSpan = null; foreach (var decl in decls) { var declSpan = decl.DeclaredNode.Span; if (declSpan.Contains(span) && (!minContainingSpan.HasValue || minContainingSpan.Value.Contains(declSpan))) { minContainingSpan = declSpan; targetSymbol = decl.DeclaredSymbol; } } } } } } if (targetSymbol == null) { // Outside of a member declaration, suppress diagnostic for the entire assembly. targetSymbol = semanticModel.Compilation.Assembly; } return new SuppressionTargetInfo() { TargetSymbol = targetSymbol, NodeWithTokens = nodeWithTokens, StartToken = startToken, EndToken = endToken }; }
public void TextSpan_DoesNotContainEnd() { var textSpan = new TextSpan(null, 1, 2); Assert.False(textSpan.Contains(textSpan.End)); }
public void TextSpanContains01() { TextSpan span_05_15 = new TextSpan(5, 10); TextSpan span_03_10 = new TextSpan(3, 7); TextSpan span_10_11 = new TextSpan(10, 1); TextSpan span_00_03 = new TextSpan(0, 3); // nonoverlapping Assert.False(span_05_15.Contains(span_00_03)); Assert.False(span_00_03.Contains(span_05_15)); // overlap with slop Assert.True(span_05_15.Contains(span_10_11)); // same span Assert.True(span_05_15.Contains(span_05_15)); // partial overlap Assert.False(span_05_15.Contains(span_03_10)); Assert.False(span_03_10.Contains(span_05_15)); }
private bool TryGetSubTextChange( SourceText originalText, TextSpan visibleSpanInOriginalText, string rightText, TextSpan spanInOriginalText, TextSpan spanInRightText, out TextChange textChange) { textChange = default(TextChange); var visibleFirstLineInOriginalText = originalText.Lines.GetLineFromPosition(visibleSpanInOriginalText.Start); var visibleLastLineInOriginalText = originalText.Lines.GetLineFromPosition(visibleSpanInOriginalText.End); // skip easy case // 1. things are out of visible span if (!visibleSpanInOriginalText.IntersectsWith(spanInOriginalText)) { return false; } // 2. there are no intersects var snippetInRightText = rightText.Substring(spanInRightText.Start, spanInRightText.Length); if (visibleSpanInOriginalText.Contains(spanInOriginalText) && visibleSpanInOriginalText.End != spanInOriginalText.End) { textChange = new TextChange(spanInOriginalText, snippetInRightText); return true; } // okay, more complex case. things are intersecting boundaries. var firstLineOfRightTextSnippet = snippetInRightText.GetFirstLineText(); var lastLineOfRightTextSnippet = snippetInRightText.GetLastLineText(); // there are 4 complex cases - these are all heuristic. not sure what better way I have. and the heristic is heavily based on // text differ's behavior. // 1. it is a single line if (visibleFirstLineInOriginalText.LineNumber == visibleLastLineInOriginalText.LineNumber) { // don't do anything return false; } // 2. replacement contains visible spans if (spanInOriginalText.Contains(visibleSpanInOriginalText)) { // header // don't do anything // body textChange = new TextChange( TextSpan.FromBounds(visibleFirstLineInOriginalText.EndIncludingLineBreak, visibleLastLineInOriginalText.Start), snippetInRightText.Substring(firstLineOfRightTextSnippet.Length, snippetInRightText.Length - firstLineOfRightTextSnippet.Length - lastLineOfRightTextSnippet.Length)); // footer // don't do anything return true; } // 3. replacement intersects with start if (spanInOriginalText.Start < visibleSpanInOriginalText.Start && visibleSpanInOriginalText.Start <= spanInOriginalText.End && spanInOriginalText.End < visibleSpanInOriginalText.End) { // header // don't do anything // body if (visibleFirstLineInOriginalText.EndIncludingLineBreak <= spanInOriginalText.End) { textChange = new TextChange( TextSpan.FromBounds(visibleFirstLineInOriginalText.EndIncludingLineBreak, spanInOriginalText.End), snippetInRightText.Substring(firstLineOfRightTextSnippet.Length)); return true; } return false; } // 4. replacement intersects with end if (visibleSpanInOriginalText.Start < spanInOriginalText.Start && spanInOriginalText.Start <= visibleSpanInOriginalText.End && visibleSpanInOriginalText.End <= spanInOriginalText.End) { // body if (spanInOriginalText.Start <= visibleLastLineInOriginalText.Start) { textChange = new TextChange( TextSpan.FromBounds(spanInOriginalText.Start, visibleLastLineInOriginalText.Start), snippetInRightText.Substring(0, snippetInRightText.Length - lastLineOfRightTextSnippet.Length)); return true; } // footer // don't do anything return false; } // if it got hit, then it means there is a missing case throw ExceptionUtilities.Unreachable; }
public void TextSpan_ContainsItself() { var textSpan = new TextSpan(null, 1, 2); Assert.True(textSpan.Contains(textSpan)); }
protected virtual IEnumerable<SyntaxNode> GetNodes(SyntaxNode root, TextSpan span) { IEnumerable<SyntaxNode> nodes; nodes = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors<SyntaxNode>().Where(a => span.Contains(a.Span)).Reverse(); return nodes; }
public async Task<IEnumerable<CodeFix>> GetSuppressionsAsync(Document document, TextSpan span, IEnumerable<Diagnostic> diagnostics, CancellationToken cancellationToken) { var suppressableDiagnostics = diagnostics.Where(CanBeSuppressed); if (suppressableDiagnostics.IsEmpty()) { return null; } var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (syntaxTree.GetLineVisibility(span.Start, cancellationToken) == LineVisibility.Hidden) { return null; } // Find the start token to attach leading pragma disable warning directive. var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); SyntaxTrivia containingTrivia = root.FindTrivia(span.Start); var lines = syntaxTree.GetText(cancellationToken).Lines; int indexOfLine; if (containingTrivia == default(SyntaxTrivia)) { indexOfLine = lines.IndexOf(span.Start); } else { indexOfLine = lines.IndexOf(containingTrivia.Token.SpanStart); } var lineAtPos = lines[indexOfLine]; var startToken = root.FindToken(lineAtPos.Start); startToken = GetAdjustedTokenForPragmaDisable(startToken, root, lines, indexOfLine); // Find the end token to attach pragma restore warning directive. // This should be the last token on the line that contains the start token. indexOfLine = lines.IndexOf(startToken.Span.End); lineAtPos = lines[indexOfLine]; var endToken = root.FindToken(lineAtPos.End); endToken = GetAdjustedTokenForPragmaRestore(endToken, root, lines, indexOfLine); SyntaxNode nodeWithTokens = null; if (IsEndOfFileToken(endToken)) { nodeWithTokens = root; } else { nodeWithTokens = startToken.GetCommonRoot(endToken); } var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>(); ISymbol targetSymbol = null; SyntaxNode targetMemberNode = null; var suppressMessageAttribute = semanticModel.Compilation.SuppressMessageAttributeType(); bool skipSuppressMessage = suppressMessageAttribute == null || !suppressMessageAttribute.IsAttribute(); if (!skipSuppressMessage) { targetMemberNode = syntaxFacts.GetContainingMemberDeclaration(root, startToken.SpanStart); if (targetMemberNode != null) { targetSymbol = semanticModel.GetDeclaredSymbol(targetMemberNode, cancellationToken); if (targetSymbol == null) { var analyzerDriverService = document.GetLanguageService<IAnalyzerDriverService>(); // targetMemberNode could be a declaration node with multiple decls (e.g. field declaration defining multiple variables). // Let us compute all the declarations intersecting the span. var decls = analyzerDriverService.GetDeclarationsInSpan(semanticModel, span, true, cancellationToken); if (decls.Any()) { var containedDecls = decls.Where(d => span.Contains(d.DeclaredNode.Span)); if (containedDecls.Count() == 1) { // Single containing declaration, use this symbol. var decl = containedDecls.Single(); targetSymbol = decl.DeclaredSymbol; } else { // Otherwise, use the most enclosing declaration. TextSpan? minContainingSpan = null; foreach (var decl in decls) { var declSpan = decl.DeclaredNode.Span; if (declSpan.Contains(span) && (!minContainingSpan.HasValue || minContainingSpan.Value.Contains(declSpan))) { minContainingSpan = declSpan; targetSymbol = decl.DeclaredSymbol; } } } } } } if (targetSymbol == null) { // Outside of a member declaration, suppress diagnostic for the entire assembly. targetSymbol = semanticModel.Compilation.Assembly; } } var result = new List<CodeFix>(); foreach (var diagnostic in suppressableDiagnostics) { var nestedActions = new List<CodeAction>(); // pragma warning disable. nestedActions.Add(new PragmaWarningCodeAction(this, startToken, endToken, nodeWithTokens, document, diagnostic)); // SuppressMessageAttribute suppression is not supported for compiler diagnostics. if (!skipSuppressMessage && !IsCompilerDiagnostic(diagnostic)) { // local member-level suppress message attribute. if (targetMemberNode != null && targetSymbol.Kind != SymbolKind.Namespace) { nestedActions.Add(new LocalSuppressMessageCodeAction(this, targetSymbol, targetMemberNode, document, diagnostic)); } // global assembly-level suppress message attribute. nestedActions.Add(new GlobalSuppressMessageCodeAction(this, targetSymbol, document, diagnostic)); } result.Add(new CodeFix(new SuppressionCodeAction(diagnostic, nestedActions), diagnostic)); } return result; }