public static PragmaRemoveAction Create( SuppressionTargetInfo suppressionTargetInfo, Document document, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer) { // We need to normalize the leading trivia on start token to account for // the trailing trivia on its previous token (and similarly normalize trailing trivia for end token). PragmaHelpers.NormalizeTriviaOnTokens(fixer, ref document, ref suppressionTargetInfo); return(new PragmaRemoveAction(suppressionTargetInfo, document, diagnostic, fixer)); }
private PragmaWarningCodeAction( SuppressionTargetInfo suppressionTargetInfo, Document document, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, bool forFixMultipleContext = false) : base(fixer, title: FeaturesResources.in_Source) { _suppressionTargetInfo = suppressionTargetInfo; _document = document; _diagnostic = diagnostic; _forFixMultipleContext = forFixMultipleContext; }
internal static SyntaxToken GetNewEndTokenWithAddedPragma( SyntaxToken endToken, TextSpan currentDiagnosticSpan, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, Func <SyntaxNode, SyntaxNode> formatNode, bool isRemoveSuppression = false) { ImmutableArray <SyntaxTrivia> trivia; var isEOF = fixer.IsEndOfFileToken(endToken); if (isEOF) { trivia = endToken.LeadingTrivia.ToImmutableArray(); } else { trivia = endToken.TrailingTrivia.ToImmutableArray(); } var index = GetPositionForPragmaInsertion(trivia, currentDiagnosticSpan, fixer, isStartToken: false, triviaAtIndex: out var insertBeforeTrivia); bool needsTrailingEOL; if (index < trivia.Length) { needsTrailingEOL = !IsEndOfLineOrHasLeadingEndOfLine(insertBeforeTrivia, fixer); } else if (isEOF) { needsTrailingEOL = false; } else { needsTrailingEOL = true; } var pragmaTrivia = !isRemoveSuppression ? fixer.CreatePragmaRestoreDirectiveTrivia(diagnostic, formatNode, needsLeadingEndOfLine : true, needsTrailingEndOfLine : needsTrailingEOL) : fixer.CreatePragmaDisableDirectiveTrivia(diagnostic, formatNode, needsLeadingEndOfLine: true, needsTrailingEndOfLine: needsTrailingEOL); if (isEOF) { return(endToken.WithLeadingTrivia(trivia.InsertRange(index, pragmaTrivia))); } else { return(endToken.WithTrailingTrivia(trivia.InsertRange(index, pragmaTrivia))); }; }
public static CodeAction CreateBatchPragmaFix( AbstractSuppressionCodeFixProvider suppressionFixProvider, Document document, ImmutableArray <IPragmaBasedCodeAction> pragmaActions, ImmutableArray <Diagnostic> pragmaDiagnostics, FixAllState fixAllState, CancellationToken cancellationToken) { // This is a temporary generated code action, which doesn't need telemetry, hence suppressing RS0005. #pragma warning disable RS0005 // Do not use generic CodeAction.Create to create CodeAction return(CodeAction.Create( ((CodeAction)pragmaActions[0]).Title, createChangedDocument: ct => BatchPragmaFixesAsync(suppressionFixProvider, document, pragmaActions, pragmaDiagnostics, cancellationToken), equivalenceKey: fixAllState.CodeActionEquivalenceKey)); #pragma warning restore RS0005 // Do not use generic CodeAction.Create to create CodeAction }
private static async Task <Solution> CreateChangedSolutionAsync(AbstractSuppressionCodeFixProvider fixer, Project triggerProject, ImmutableDictionary <Project, ImmutableArray <Diagnostic> > diagnosticsByProject, CancellationToken cancellationToken) { var currentSolution = triggerProject.Solution; foreach (var kvp in diagnosticsByProject) { var oldProject = kvp.Key; var currentProject = currentSolution.GetProject(oldProject.Id); var diagnosticsBySymbol = await CreateDiagnosticsBySymbolAsync(fixer, oldProject, kvp.Value, cancellationToken).ConfigureAwait(false); if (diagnosticsBySymbol.Any()) { var projectCodeAction = new GlobalSuppressMessageFixAllCodeAction(fixer, diagnosticsBySymbol, currentProject); var newDocument = await projectCodeAction.GetChangedSuppressionDocumentAsync(cancellationToken).ConfigureAwait(false); currentSolution = newDocument.Project.Solution; } } return(currentSolution); }
internal static CodeAction Create(string title, AbstractSuppressionCodeFixProvider fixer, Project triggerProject, ImmutableDictionary <Project, ImmutableArray <Diagnostic> > diagnosticsByProject) { return(new GlobalSuppressionSolutionChangeAction(title, ct => CreateChangedSolutionAsync(fixer, triggerProject, diagnosticsByProject, ct), equivalenceKey: title)); }
private GlobalSuppressMessageFixAllCodeAction(AbstractSuppressionCodeFixProvider fixer, IEnumerable <KeyValuePair <ISymbol, ImmutableArray <Diagnostic> > > diagnosticsBySymbol, Project project) : base(fixer, project) { _diagnosticsBySymbol = diagnosticsBySymbol; }
private static async Task <IEnumerable <KeyValuePair <ISymbol, ImmutableArray <Diagnostic> > > > CreateDiagnosticsBySymbolAsync(AbstractSuppressionCodeFixProvider fixer, Project project, ImmutableArray <Diagnostic> diagnostics, CancellationToken cancellationToken) { var diagnosticsMapBuilder = ImmutableDictionary.CreateBuilder <ISymbol, List <Diagnostic> >(); var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); if (compilation != null) { foreach (var diagnostic in diagnostics) { Contract.ThrowIfFalse(!diagnostic.Location.IsInSource); var targetSymbol = compilation.Assembly; AddDiagnosticForSymbolIfNeeded(targetSymbol, diagnostic, diagnosticsMapBuilder); } } return(CreateDiagnosticsBySymbol(diagnosticsMapBuilder)); }
private static async Task <IEnumerable <KeyValuePair <ISymbol, ImmutableArray <Diagnostic> > > > CreateDiagnosticsBySymbolAsync(AbstractSuppressionCodeFixProvider fixer, IEnumerable <KeyValuePair <Document, ImmutableArray <Diagnostic> > > diagnosticsByDocument, CancellationToken cancellationToken) { var diagnosticsMapBuilder = ImmutableDictionary.CreateBuilder <ISymbol, List <Diagnostic> >(); foreach (var kvp in diagnosticsByDocument) { var document = kvp.Key; var diagnostics = kvp.Value; foreach (var diagnostic in diagnostics) { Contract.ThrowIfFalse(diagnostic.Location.IsInSource); var suppressionTargetInfo = await fixer.GetSuppressionTargetInfoAsync(document, diagnostic.Location.SourceSpan, cancellationToken).ConfigureAwait(false); if (suppressionTargetInfo != null) { var targetSymbol = suppressionTargetInfo.TargetSymbol; Contract.ThrowIfNull(targetSymbol); AddDiagnosticForSymbolIfNeeded(targetSymbol, diagnostic, diagnosticsMapBuilder); } } } return(CreateDiagnosticsBySymbol(diagnosticsMapBuilder)); }
protected AbstractGlobalSuppressMessageCodeAction(AbstractSuppressionCodeFixProvider fixer, Project project) : base(fixer, title: FeaturesResources.in_Suppression_File) { _project = project; }
public PragmaWarningBatchFixAllProvider(AbstractSuppressionCodeFixProvider suppressionFixProvider) { _suppressionFixProvider = suppressionFixProvider; }
private static bool IsEndOfLineOrHasTrailingEndOfLine(SyntaxTrivia trivia, AbstractSuppressionCodeFixProvider fixer) { return(fixer.IsEndOfLine(trivia) || (trivia.HasStructure && fixer.IsEndOfLine(trivia.GetStructure().DescendantTrivia().LastOrDefault()))); }
private static SyntaxToken UpdateTriviaList(SyntaxToken token, bool isStartToken, SyntaxTriviaList triviaList, AbstractSuppressionCodeFixProvider fixer) { return(isStartToken || fixer.IsEndOfFileToken(token) ? token.WithLeadingTrivia(triviaList) : token.WithTrailingTrivia(triviaList)); }
public static BatchFixAllProvider GetBatchFixer(AbstractSuppressionCodeFixProvider suppressionFixProvider) { return(new BatchFixer(suppressionFixProvider)); }
private static int GetPositionForPragmaInsertion(ImmutableArray <SyntaxTrivia> triviaList, TextSpan currentDiagnosticSpan, AbstractSuppressionCodeFixProvider fixer, bool isStartToken, out SyntaxTrivia triviaAtIndex) { // Start token: Insert the #pragma disable directive just **before** the first end of line trivia prior to diagnostic location. // End token: Insert the #pragma disable directive just **after** the first end of line trivia after diagnostic location. int getNextIndex(int cur) => isStartToken ? cur - 1 : cur + 1; bool shouldConsiderTrivia(SyntaxTrivia trivia) => isStartToken ? trivia.FullSpan.End <= currentDiagnosticSpan.Start : trivia.FullSpan.Start >= currentDiagnosticSpan.End; var walkedPastDiagnosticSpan = false; var seenEndOfLineTrivia = false; var index = isStartToken ? triviaList.Length - 1 : 0; while (index >= 0 && index < triviaList.Length) { var trivia = triviaList[index]; walkedPastDiagnosticSpan = walkedPastDiagnosticSpan || shouldConsiderTrivia(trivia); seenEndOfLineTrivia = seenEndOfLineTrivia || IsEndOfLineOrContainsEndOfLine(trivia, fixer); if (walkedPastDiagnosticSpan && seenEndOfLineTrivia) { break; } index = getNextIndex(index); } triviaAtIndex = index >= 0 && index < triviaList.Length ? triviaList[index] : default; return(index); }
internal static void NormalizeTriviaOnTokens(AbstractSuppressionCodeFixProvider fixer, ref Document document, ref SuppressionTargetInfo suppressionTargetInfo) { // For pragma suppression fixes, we need to normalize the leading trivia on start token to account for // the trailing trivia on its previous token (and similarly normalize trailing trivia for end token). var startToken = suppressionTargetInfo.StartToken; var endToken = suppressionTargetInfo.EndToken; var nodeWithTokens = suppressionTargetInfo.NodeWithTokens; var startAndEndTokensAreSame = startToken == endToken; var isEndTokenEOF = fixer.IsEndOfFileToken(endToken); var previousOfStart = startToken.GetPreviousToken(includeZeroWidth: true); var nextOfEnd = !isEndTokenEOF?endToken.GetNextToken(includeZeroWidth : true) : default; if (!previousOfStart.HasTrailingTrivia && !nextOfEnd.HasLeadingTrivia) { return; } var root = nodeWithTokens.SyntaxTree.GetRoot(); var spanEnd = !isEndTokenEOF ? nextOfEnd.FullSpan.End : endToken.FullSpan.End; var subtreeRoot = root.FindNode(new TextSpan(previousOfStart.FullSpan.Start, spanEnd - previousOfStart.FullSpan.Start)); var currentStartToken = startToken; var currentEndToken = endToken; var newStartToken = startToken.WithLeadingTrivia(previousOfStart.TrailingTrivia.Concat(startToken.LeadingTrivia)); SyntaxToken newEndToken = currentEndToken; if (startAndEndTokensAreSame) { newEndToken = newStartToken; } newEndToken = newEndToken.WithTrailingTrivia(endToken.TrailingTrivia.Concat(nextOfEnd.LeadingTrivia)); var newPreviousOfStart = previousOfStart.WithTrailingTrivia(); var newNextOfEnd = nextOfEnd.WithLeadingTrivia(); var newSubtreeRoot = subtreeRoot.ReplaceTokens(new[] { startToken, previousOfStart, endToken, nextOfEnd }, (o, n) => { if (o == currentStartToken) { return(startAndEndTokensAreSame ? newEndToken : newStartToken); } else if (o == previousOfStart) { return(newPreviousOfStart); } else if (o == currentEndToken) { return(newEndToken); } else if (o == nextOfEnd) { return(newNextOfEnd); } else { return(n); } }); root = root.ReplaceNode(subtreeRoot, newSubtreeRoot); document = document.WithSyntaxRoot(root); suppressionTargetInfo.StartToken = root.FindToken(startToken.SpanStart); suppressionTargetInfo.EndToken = root.FindToken(endToken.SpanStart); suppressionTargetInfo.NodeWithTokens = fixer.GetNodeWithTokens(suppressionTargetInfo.StartToken, suppressionTargetInfo.EndToken, root); }
private static bool CanRemovePragmaTrivia(SyntaxToken token, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, bool isStartToken, out int indexOfTriviaToRemove) { indexOfTriviaToRemove = -1; var triviaList = GetTriviaListForSuppression(token, isStartToken, fixer); var diagnosticSpan = diagnostic.Location.SourceSpan; bool shouldIncludeTrivia(SyntaxTrivia t) => isStartToken ? t.FullSpan.End <= diagnosticSpan.Start : t.FullSpan.Start >= diagnosticSpan.End; var filteredTriviaList = triviaList.Where(shouldIncludeTrivia); if (isStartToken) { // Walk bottom up for leading trivia. filteredTriviaList = filteredTriviaList.Reverse(); } foreach (var trivia in filteredTriviaList) { if (fixer.IsAnyPragmaDirectiveForId(trivia, diagnostic.Id, out var isEnableDirective, out var hasMultipleIds)) { if (hasMultipleIds) { // Handle only simple cases where we have a single pragma directive with single ID matching ours in the trivia. return(false); } // We want to look for leading disable directive and trailing enable directive. if ((isStartToken && !isEnableDirective) || (!isStartToken && isEnableDirective)) { indexOfTriviaToRemove = triviaList.IndexOf(trivia); return(true); } return(false); } } return(false); }
private static bool IsEndOfLineOrContainsEndOfLine(SyntaxTrivia trivia, AbstractSuppressionCodeFixProvider fixer) { return(fixer.IsEndOfLine(trivia) || (trivia.HasStructure && trivia.GetStructure().DescendantTrivia().Any(t => fixer.IsEndOfLine(t)))); }
public GlobalSuppressMessageCodeAction(ISymbol targetSymbol, Project project, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer) : base(fixer, project) { _targetSymbol = targetSymbol; _diagnostic = diagnostic; }
private static async Task <Document> BatchPragmaFixesAsync( AbstractSuppressionCodeFixProvider suppressionFixProvider, Document document, ImmutableArray <IPragmaBasedCodeAction> pragmaActions, ImmutableArray <Diagnostic> diagnostics, CancellationToken cancellationToken) { // We apply all the pragma suppression fixes sequentially. // At every application, we track the updated locations for remaining diagnostics in the document. var currentDiagnosticSpans = new Dictionary <Diagnostic, TextSpan>(); foreach (var diagnostic in diagnostics) { currentDiagnosticSpans.Add(diagnostic, diagnostic.Location.SourceSpan); } var currentDocument = document; for (int i = 0; i < pragmaActions.Length; i++) { var originalpragmaAction = pragmaActions[i]; var diagnostic = diagnostics[i]; // Get the diagnostic span for the diagnostic in latest document snapshot. if (!currentDiagnosticSpans.TryGetValue(diagnostic, out var currentDiagnosticSpan)) { // Diagnostic whose location conflicts with a prior fix. continue; } // Compute and apply pragma suppression fix. var currentTree = await currentDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var currentLocation = Location.Create(currentTree, currentDiagnosticSpan); diagnostic = Diagnostic.Create( id: diagnostic.Id, category: diagnostic.Descriptor.Category, message: diagnostic.GetMessage(), severity: diagnostic.Severity, defaultSeverity: diagnostic.DefaultSeverity, isEnabledByDefault: diagnostic.Descriptor.IsEnabledByDefault, warningLevel: diagnostic.WarningLevel, title: diagnostic.Descriptor.Title, description: diagnostic.Descriptor.Description, helpLink: diagnostic.Descriptor.HelpLinkUri, location: currentLocation, additionalLocations: diagnostic.AdditionalLocations, customTags: diagnostic.Descriptor.CustomTags, properties: diagnostic.Properties, isSuppressed: diagnostic.IsSuppressed); var newSuppressionFixes = await suppressionFixProvider.GetSuppressionsAsync(currentDocument, currentDiagnosticSpan, SpecializedCollections.SingletonEnumerable(diagnostic), cancellationToken).ConfigureAwait(false); var newSuppressionFix = newSuppressionFixes.SingleOrDefault(); if (newSuppressionFix != null) { var newPragmaAction = newSuppressionFix.Action as IPragmaBasedCodeAction ?? newSuppressionFix.Action.NestedCodeActions.OfType <IPragmaBasedCodeAction>().SingleOrDefault(); if (newPragmaAction != null) { // Get the text changes with pragma suppression add/removals. // Note: We do it one token at a time to ensure we get single text change in the new document, otherwise UpdateDiagnosticSpans won't function as expected. // Update the diagnostics spans based on the text changes. var startTokenChanges = await GetTextChangesAsync(newPragmaAction, currentDocument, diagnostics, currentDiagnosticSpans, includeStartTokenChange : true, includeEndTokenChange : false, cancellationToken : cancellationToken).ConfigureAwait(false); var endTokenChanges = await GetTextChangesAsync(newPragmaAction, currentDocument, diagnostics, currentDiagnosticSpans, includeStartTokenChange : false, includeEndTokenChange : true, cancellationToken : cancellationToken).ConfigureAwait(false); var currentText = await currentDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); var orderedChanges = startTokenChanges.Concat(endTokenChanges).OrderBy(change => change.Span).Distinct(); var newText = currentText.WithChanges(orderedChanges); currentDocument = currentDocument.WithText(newText); // Update the diagnostics spans based on the text changes. UpdateDiagnosticSpans(diagnostics, currentDiagnosticSpans, orderedChanges); } } } return(currentDocument); }
private static SyntaxToken GetNewTokenWithPragmaUnsuppress(SyntaxToken token, int indexOfTriviaToRemoveOrToggle, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, bool isStartToken, bool toggle) { Contract.ThrowIfFalse(indexOfTriviaToRemoveOrToggle >= 0); var triviaList = GetTriviaListForSuppression(token, isStartToken, fixer); if (toggle) { var triviaToToggle = triviaList.ElementAt(indexOfTriviaToRemoveOrToggle); Contract.ThrowIfFalse(triviaToToggle != default); var toggledTrivia = fixer.TogglePragmaDirective(triviaToToggle); triviaList = triviaList.Replace(triviaToToggle, toggledTrivia); } else { triviaList = triviaList.RemoveAt(indexOfTriviaToRemoveOrToggle); } return(UpdateTriviaList(token, isStartToken, triviaList, fixer)); }
public BatchFixer(AbstractSuppressionCodeFixProvider suppressionFixProvider) { _suppressionFixProvider = suppressionFixProvider; }
private static SyntaxTriviaList GetTriviaListForSuppression(SyntaxToken token, bool isStartToken, AbstractSuppressionCodeFixProvider fixer) { return(isStartToken || fixer.IsEndOfFileToken(token) ? token.LeadingTrivia : token.TrailingTrivia); }