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));
                }
Beispiel #2
0
 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)));
                };
            }
Beispiel #4
0
            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));
 }
Beispiel #14
0
 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;
 }
Beispiel #20
0
            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));
                }
Beispiel #22
0
 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);
 }