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;
}
}