Exemplo n.º 1
0
        public SuppressSuggestedAction(DevSkimError error, int days = -1, bool suppressAll = false)
        {
            _error = error;
            _rule  = error.Rule;

            _span     = _error.ErrorTrackingSpan;
            _snapshot = _error.Snapshot;
            _code     = _error.LineTrackingSpan.GetText(_snapshot);

            if (_rule != null)
            {
                if (days > 0)
                {
                    _display  = string.Format(Resources.Messages.SuppressIssue, _rule.Id, days);
                    _suppDate = DateTime.Now.AddDays(days);
                }
                else
                {
                    _display = string.Format(Resources.Messages.SuppressIssuePermanently, _rule.Id);
                }
            }
            else
            {
                if (days > 0)
                {
                    _display  = string.Format(Resources.Messages.SupressAllIssues, days);
                    _suppDate = DateTime.Now.AddDays(days);
                }
                else
                {
                    _display = string.Format(Resources.Messages.SuppressAllIssuesPermanently);
                }
            }
        }
Exemplo n.º 2
0
 public SuppressionEx(DevSkimError error, string language)
     : base(text: new TextContainer(error.Snapshot.GetText(), error.Snapshot.ContentType.TypeName), lineNumber: error.LineNumber + 1)
 {
     _error    = error;
     _language = language;
     ParseOtherLineSuppressions();
 }
Exemplo n.º 3
0
        public Task <bool> HasSuggestedActionsAsync(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
        {
            return(Task.Factory.StartNew(() =>
            {
                DevSkimError error = GetErrorUnderCaret(range);

                return (error != null && error.Actionable);
            }));
        }
Exemplo n.º 4
0
        /// <summary>
        ///     This is where we get the lines for parsing
        /// </summary>
        private void DoUpdate()
        {
            // It would be good to do all of this work on a background thread but we can't:
            // _classifier.GetClassificationSpans() should only be called on the UI thread because some
            // classifiers assume they are called from the UI thread. Raising the TagsChanged event from the
            // taggers needs to happen on the UI thread (because some consumers might assume it is being
            // raised on the UI thread).
            //
            // Updating the snapshot for the factory and calling the sink can happen on any thread but those
            // operations are so fast that there is no point.
            if ((!_isDisposed) && (_dirtySpans.Count > 0))
            {
                var line = _dirtySpans[0].Start.GetContainingLine();

                if (line.Length > 0)
                {
                    var oldSecurityErrors = Factory.CurrentSnapshot;
                    var newSecurityErrors = new DevSkimErrorsSnapshot(this.FilePath, oldSecurityErrors.VersionNumber + 1);

                    bool anyNewErrors = false;

                    string text = _textView.TextSnapshot.GetText();

                    Issue[] issues = SkimShim.Analyze(text, line.Snapshot.ContentType.TypeName, FilePath);
                    // Don't populate suppressed issues
                    foreach (Issue issue in issues.Where(x => !x.IsSuppressionInfo))
                    {
                        int errorStart  = issue.StartLocation.Column;
                        int errorLength = issue.Boundary.Length;
                        if (errorLength > 0)    // Ignore any single character error.
                        {
                            line = _textView.TextSnapshot.GetLineFromLineNumber(issue.StartLocation.Line - 1);
                            var newSpan  = new SnapshotSpan(line.Start + errorStart - 1, errorLength);
                            var oldError = oldSecurityErrors.Errors.Find((e) => e.Span == newSpan);

                            if (oldError != null)
                            {
                                // There was a security error at the same span as the old one so we should be
                                // able to just reuse it.
                                oldError.NextIndex = newSecurityErrors.Errors.Count;
                                newSecurityErrors.Errors.Add(DevSkimError.Clone(oldError));    // Don't clone the old error yet
                            }
                            else
                            {
                                newSecurityErrors.Errors.Add(new DevSkimError(newSpan, issue.Rule, !issue.IsSuppressionInfo));
                            }
                        }
                    }

                    UpdateSecurityErrors(newSecurityErrors);
                }

                _dirtySpans = new NormalizedSnapshotSpanCollection(line.Snapshot, new NormalizedSpanCollection());
                _isUpdating = false;
            }
        }
Exemplo n.º 5
0
 public SuppressionEx(DevSkimError error, string language)
     : base(text: new TextContainer(error.Snapshot.GetText(), error.Snapshot.ContentType.TypeName), lineNumber: error.LineNumber + 1)
 {
     _error    = error;
     _language = language;
     foreach (SuppressedIssue issue in base.GetIssues())
     {
         _issues.Add(issue.ID);
     }
 }
Exemplo n.º 6
0
        public static DevSkimError CloneAndTranslateTo(DevSkimError error, ITextSnapshot newSnapshot)
        {
            var newSpan = error.Span.TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive);

            // We want to only translate the error if the length of the error span did not change (if it did change, it would imply that
            // there was some text edit inside the error and, therefore, that the error is no longer valid).
            return((newSpan.Length == error.Span.Length)
                   ? new DevSkimError(newSpan, error.Rule, error.Actionable)
                   : null);
        }
Exemplo n.º 7
0
        public IEnumerable <SuggestedActionSet> GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
        {
            DevSkimError error = GetErrorUnderCaret(range);

            if (error != null && error.Actionable)
            {
                List <ISuggestedAction> fixActions = new List <ISuggestedAction>();

                ITrackingSpan trackingSpan = range.Snapshot.CreateTrackingSpan(error.Span, SpanTrackingMode.EdgeInclusive);

                // Create list of fixes if the rule has them..
                if (error.Rule.Fixes != null)
                {
                    foreach (CodeFix fix in error.Rule.Fixes)
                    {
                        fixActions.Add(new FixSuggestedAction(trackingSpan, error.Rule, fix));
                    }
                }

                int suppressDays = Settings.GetSettings().SuppressDays;

                List <ISuggestedAction> suppActions = new List <ISuggestedAction>();
                var line = range.Snapshot.GetLineFromPosition(range.Start);
                trackingSpan = line.Snapshot.CreateTrackingSpan(line.Extent, SpanTrackingMode.EdgeInclusive);

                suppActions.Add(new SuppressSuggestedAction(trackingSpan, error.Rule, suppressDays));
                suppActions.Add(new SuppressSuggestedAction(trackingSpan, error.Rule));

                // If there is multiple issues on the line, offer "Suppress all"
                if (SkimShim.HasMultipleProblems(trackingSpan.GetText(range.Snapshot),
                                                 trackingSpan.TextBuffer.ContentType.TypeName))
                {
                    suppActions.Add(new SuppressSuggestedAction(trackingSpan, null, suppressDays));
                    suppActions.Add(new SuppressSuggestedAction(trackingSpan, null));
                }

                VSPackage.LogEvent(string.Format("Lightbulb invoked on {0} {1}", error.Rule.Id, error.Rule.Name));

                // We don't want empty group and spacer in the pop-up menu
                if (fixActions.Count > 0)
                {
                    return new SuggestedActionSet[] { new SuggestedActionSet(fixActions), new SuggestedActionSet(suppActions) }
                }
                ;
                else
                {
                    return new SuggestedActionSet[] { new SuggestedActionSet(suppActions) }
                };
            }

            return(Enumerable.Empty <SuggestedActionSet>());
        }
Exemplo n.º 8
0
        private void OnBufferChange(object sender, TextContentChangedEventArgs e)
        {
            _currentSnapshot = e.After;

            // Translate all of the old dirty spans to the new snapshot.
            NormalizedSnapshotSpanCollection newDirtySpans = _dirtySpans.CloneAndTrackTo(e.After, SpanTrackingMode.EdgeInclusive);

            // Dirty all the spans that changed.
            foreach (var change in e.Changes)
            {
                newDirtySpans = NormalizedSnapshotSpanCollection.Union(newDirtySpans, new NormalizedSnapshotSpanCollection(e.After, change.NewSpan));
            }

            // Translate all the security errors to the new snapshot (and remove anything that is a dirty
            // region since we will need to check that again).
            var oldSecurityErrors = this.Factory.CurrentSnapshot;
            var newSecurityErrors = new DevSkimErrorsSnapshot(this.FilePath, oldSecurityErrors.VersionNumber + 1);

            // Copy all of the old errors to the new errors unless the error was affected by the text change
            foreach (var error in oldSecurityErrors.Errors)
            {
                Debug.Assert(error.NextIndex == -1);

                var newError = DevSkimError.CloneAndTranslateTo(error, e.After);

                if (newError != null)
                {
                    Debug.Assert(newError.Span.Length == error.Span.Length);

                    error.NextIndex = newSecurityErrors.Errors.Count;
                    newSecurityErrors.Errors.Add(newError);
                }
            }

            this.UpdateSecurityErrors(newSecurityErrors);

            _dirtySpans = newDirtySpans;

            // Start processing the dirty spans (which no-ops if we're already doing it).
            if (_dirtySpans.Count != 0)
            {
                this.KickUpdate();
            }
        }
        /// <summary>
        /// This is where we get the lines for parsing
        /// </summary>
        private void DoUpdate()
        {
            // It would be good to do all of this work on a background thread but we can't:
            //      _classifier.GetClassificationSpans() should only be called on the UI thread because some classifiers assume they are called from the UI thread.
            //      Raising the TagsChanged event from the taggers needs to happen on the UI thread (because some consumers might assume it is being raised on the UI thread).
            //
            // Updating the snapshot for the factory and calling the sink can happen on any thread but those operations are so fast that there is no point.
            if ((!_isDisposed) && (_dirtySpans.Count > 0))
            {
                var line = _dirtySpans[0].Start.GetContainingLine();

                if (line.Length > 0)
                {
                    var oldSecurityErrors = this.Factory.CurrentSnapshot;
                    var newSecurityErrors = new DevSkimErrorsSnapshot(this.FilePath, oldSecurityErrors.VersionNumber + 1);

                    // Go through the existing errors. If they are on the line we are currently parsing then
                    // copy them to oldLineErrors, otherwise they go to the new errors.
                    var oldLineErrors = new List <DevSkimError>();
                    foreach (var error in oldSecurityErrors.Errors)
                    {
                        Debug.Assert(error.NextIndex == -1);

                        if (line.Extent.Contains(error.Span))
                        {
                            error.NextIndex = -1;
                            oldLineErrors.Add(error); // Do not clone old error here ... we'll do that later there is no change.
                        }
                        else
                        {
                            error.NextIndex = newSecurityErrors.Errors.Count;
                            newSecurityErrors.Errors.Add(DevSkimError.Clone(error));   // We must clone the old error here.
                        }
                    }

                    int  expectedErrorCount = newSecurityErrors.Errors.Count + oldLineErrors.Count;
                    bool anyNewErrors       = false;

                    // provide whole line to processor so it has complete overview of the scanned code
                    string text = line.GetText();

                    Problem[] issues = SkimShim.Analyze(text, line.Snapshot.ContentType.TypeName, this.FilePath);
                    foreach (Problem result in issues)
                    {
                        int errorStart  = result.Issue.Index;
                        int errorLength = result.Issue.Length;
                        if (errorLength > 0)    // Ignore any single character error.
                        {
                            var newSpan  = new SnapshotSpan(line.Start + errorStart, errorLength);
                            var oldError = oldLineErrors.Find((e) => e.Span == newSpan);

                            if (oldError != null)
                            {
                                // There was a security error at the same span as the old one so we should be able to just reuse it.
                                oldError.NextIndex = newSecurityErrors.Errors.Count;
                                newSecurityErrors.Errors.Add(DevSkimError.Clone(oldError));    // Don't clone the old error yet
                            }
                            else
                            {
                                newSecurityErrors.Errors.Add(new DevSkimError(newSpan, result.Issue.Rule, result.Actionable));
                                anyNewErrors = true;
                            }
                        }
                    }

                    // This does a deep comparison so we will only do the update if the a different set of errors was discovered compared to what we had previously.
                    // If there were any new errors or if we didn't see all the expected errors then there is a change and we need to update the security errors.
                    if (anyNewErrors || (newSecurityErrors.Errors.Count != expectedErrorCount))
                    {
                        this.UpdateSecurityErrors(newSecurityErrors);
                    }
                    else
                    {
                        // There were no changes so we don't need to update our snapshot.
                        // We have, however, dirtied the old errors by setting their NextIndex property on the assumption that we would be updating the errors.
                        // Revert all those changes.
                        foreach (var error in oldSecurityErrors.Errors)
                        {
                            error.NextIndex = -1;
                        }
                    }
                }

                // NormalizedSnapshotSpanCollection.Difference doesn't quite do what we need here. If I have {[5,5), [10,20)} and subtract {[5, 15)} and do a ...Difference, I
                // end up with {[5,5), [15,20)} (the zero length span at the beginning isn't getting removed). A zero-length span at the end wouldn't be removed but, in this case,
                // that is the desired behavior (the zero length span at the end could be a change at the beginning of the next line, which we'd want to keep).
                var newDirtySpans = new List <Span>(_dirtySpans.Count + 1);
                var extent        = line.ExtentIncludingLineBreak;

                for (int i = 0; (i < _dirtySpans.Count); ++i)
                {
                    Span s = _dirtySpans[i];
                    if ((s.End < extent.Start) || (s.Start >= extent.End))          // Intentionally asymmetric
                    {
                        newDirtySpans.Add(s);
                    }
                    else
                    {
                        if (s.Start < extent.Start)
                        {
                            newDirtySpans.Add(Span.FromBounds(s.Start, extent.Start));
                        }

                        if ((s.End >= extent.End) && (extent.End < line.Snapshot.Length))
                        {
                            newDirtySpans.Add(Span.FromBounds(extent.End, s.End));  //This could add a zero length span (which is by design since we want to ensure we security check the next line).
                        }
                    }
                }

                _dirtySpans = new NormalizedSnapshotSpanCollection(line.Snapshot, newDirtySpans);

                if (_dirtySpans.Count == 0)
                {
                    // We've cleaned up all the dirty spans.
                    _isUpdating = false;
                }
                else
                {
                    // Still more work to do.
                    _uiThreadDispatcher.BeginInvoke(new Action(() => this.DoUpdate()), DispatcherPriority.Background);
                }
            }
        }
Exemplo n.º 10
0
 public static DevSkimError Clone(DevSkimError error)
 {
     return(new DevSkimError(error.Span, error.Rule, error.Actionable));
 }