diff --git a/AppInspector.RulesEngine/RuleProcessor.cs b/AppInspector.RulesEngine/RuleProcessor.cs index 7b3afa5e..f3ec8201 100644 --- a/AppInspector.RulesEngine/RuleProcessor.cs +++ b/AppInspector.RulesEngine/RuleProcessor.cs @@ -566,11 +566,11 @@ internal static string ExtractExcerpt(TextContainer text, Location start, Locati int startLineNumber = start.Line < 0 ? 0 : start.Line > text.LineEnds.Count ? text.LineEnds.Count - 1 : start.Line; - int endLineNUmber = + int endLineNumber = end.Line < 0 ? 0 : end.Line > text.LineEnds.Count ? text.LineEnds.Count - 1 : end.Line; // First we try to include the number of lines of context requested var excerptStartLine = Math.Max(0, startLineNumber - context); - var excerptEndLine = Math.Min(text.LineEnds.Count - 1, endLineNUmber + context); + var excerptEndLine = Math.Min(text.LineEnds.Count - 1, endLineNumber + context); var startIndex = text.LineStarts[excerptStartLine]; var endIndex = text.LineEnds[excerptEndLine] + 1; // Maximum number of characters to capture on each side diff --git a/AppInspector.RulesEngine/TextContainer.cs b/AppInspector.RulesEngine/TextContainer.cs index b9d8873b..f8d242be 100644 --- a/AppInspector.RulesEngine/TextContainer.cs +++ b/AppInspector.RulesEngine/TextContainer.cs @@ -449,12 +449,14 @@ public string GetLineContent(int line) /// /// Returns location (Line, Column) for given index in text + /// If the index is beyond the end of the file, clamps to the end /// /// Position in text (line is one-indexed) /// Location public Location GetLocation(int index) { for (var i = 1; i < LineEnds.Count; i++) + { if (LineEnds[i] >= index) { return new Location @@ -463,6 +465,17 @@ public Location GetLocation(int index) Line = i }; } + } + + // If the index is beyond the end of the file, clamp to the end of the file + if (index > LineEnds[^1]) + { + return new Location() + { + Column = LineEnds[^1] - LineStarts[^1], + Line = LineEnds.Count + }; + } return new Location(); } diff --git a/AppInspector/Commands/AnalyzeCommand.cs b/AppInspector/Commands/AnalyzeCommand.cs index 7fe617a1..095e4dbc 100644 --- a/AppInspector/Commands/AnalyzeCommand.cs +++ b/AppInspector/Commands/AnalyzeCommand.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.IO; @@ -771,21 +772,38 @@ private IEnumerable EnumerateFileEntries() if (contents != null) { - if (_options.DisableCrawlArchives) + IList entriesToYield = new List(); + try { - yield return new FileEntry(srcFile, contents); + if (_options.DisableCrawlArchives) + { + entriesToYield.Add(new FileEntry(srcFile, contents)); + } + else + { + // Use MemoryStreamCutoff = 1 to force using FileStream with DeleteOnClose for backing, and avoid memory exhaustion. + ExtractorOptions opts = new() + { + Parallel = false, DenyFilters = _options.FilePathExclusions, MemoryStreamCutoff = 1 + }; + // This works if the contents contain any kind of file. + // If the file is an archive this gets all the entries it contains. + // If the file is not an archive, the stream is wrapped in a FileEntry container and yielded + entriesToYield = extractor.Extract(srcFile, contents, opts).ToImmutableList(); + } } - else + catch (Exception e) { - // Use MemoryStreamCutoff = 1 to force using FileStream with DeleteOnClose for backing, and avoid memory exhaustion. - ExtractorOptions opts = new() - { - Parallel = false, DenyFilters = _options.FilePathExclusions, MemoryStreamCutoff = 1 - }; - // This works if the contents contain any kind of file. - // If the file is an archive this gets all the entries it contains. - // If the file is not an archive, the stream is wrapped in a FileEntry container and yielded - foreach (var entry in extractor.Extract(srcFile, contents, opts)) yield return entry; + _logger.LogDebug( + "Failed to analyze file {Path}. {Type}:{Message}. ({StackTrace})", + srcFile, e.GetType(), e.Message, e.StackTrace); + _metaDataHelper?.Metadata.Files.Add(new FileRecord + { FileName = srcFile, Status = ScanState.Error }); + } + + foreach (var entry in entriesToYield) + { + yield return entry; } }