Skip to content

Commit d901dfb

Browse files
committed
Fix NRT warnings in Word converter
Many of these are fixed with just pattern matching. I've been liberal with the null-forgiving operator in the Word document code itself, but most of the rest is fairly straightforward to handle safely. Fixes #998.
1 parent 1b8e8c3 commit d901dfb

10 files changed

+106
-121
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using MarkdownConverter.Spec;
1+
using MarkdownConverter.Spec;
22
using Xunit;
33

44
namespace MarkdownConverter.Tests;
@@ -8,6 +8,6 @@ public class MarkdownSpecFileListTests
88
[Fact]
99
public void EmptyListTest()
1010
{
11-
Assert.Throws<ArgumentNullException>(() => MarkdownSpec.ReadFiles(null, new Reporter(TextWriter.Null)));
11+
Assert.Throws<ArgumentNullException>(() => MarkdownSpec.ReadFiles(null!, new Reporter(TextWriter.Null)));
1212
}
13-
}
13+
}

tools/MarkdownConverter/Converter/MarkdownSourceConverter.cs

+52-61
Large diffs are not rendered by default.

tools/MarkdownConverter/Converter/MarkdownSpecConverter.cs

+18-12
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ public static void ConvertToWord(MarkdownSpec spec, string templateFile, string
1717
resultDoc.AddPart(part.OpenXmlPart, part.RelationshipId);
1818
}
1919

20-
var body = resultDoc.MainDocumentPart.Document.Body;
20+
var body = resultDoc.MainDocumentPart!.Document.Body!;
2121

2222
ReplaceTableOfContents(spec, body);
2323

2424
var context = new ConversionContext();
25-
context.MaxBookmarkId.Value = 1 + body.Descendants<BookmarkStart>().Max(bookmark => int.Parse(bookmark.Id));
25+
context.MaxBookmarkId.Value = 1 + body.Descendants<BookmarkStart>().Max(bookmark => int.Parse(bookmark.Id!));
2626

2727
foreach (var src in spec.Sources)
2828
{
@@ -72,9 +72,12 @@ private static void ReplaceTableOfContents(MarkdownSpec spec, Body body)
7272
}
7373
}
7474

75-
private static bool FindToc(Body body, out int ifirst, out int iLast, out string instr, out Paragraph secBreak)
75+
private static bool FindToc(Body body, out int ifirst, out int iLast, out string? instr, out Paragraph? secBreak)
7676
{
77-
ifirst = -1; iLast = -1; instr = null; secBreak = null;
77+
ifirst = -1;
78+
iLast = -1;
79+
instr = null;
80+
secBreak = null;
7881

7982
for (int i = 0; i < body.ChildElements.Count; i++)
8083
{
@@ -86,23 +89,25 @@ private static bool FindToc(Body body, out int ifirst, out int iLast, out string
8689

8790
// The TOC might be a simple field
8891
var sf = p.OfType<SimpleField>().FirstOrDefault();
89-
if (sf != null && sf.Instruction.Value.Contains("TOC"))
92+
if (sf?.Instruction?.Value?.Contains("TOC") == true)
9093
{
9194
if (ifirst != -1)
9295
{
9396
throw new Exception("Found start of TOC and then another simple TOC");
9497
}
9598

96-
ifirst = i; iLast = i; instr = sf.Instruction.Value;
99+
ifirst = i;
100+
iLast = i;
101+
instr = sf.Instruction.Value;
97102
break;
98103
}
99104

100105
// or it might be a complex field
101106
var runElements = (from r in p.OfType<Run>() from e in r select e).ToList();
102-
var f1 = runElements.FindIndex(f => f is FieldChar && (f as FieldChar).FieldCharType.Value == FieldCharValues.Begin);
103-
var f2 = runElements.FindIndex(f => f is FieldCode && (f as FieldCode).Text.Contains("TOC"));
104-
var f3 = runElements.FindIndex(f => f is FieldChar && (f as FieldChar).FieldCharType.Value == FieldCharValues.Separate);
105-
var f4 = runElements.FindIndex(f => f is FieldChar && (f as FieldChar).FieldCharType.Value == FieldCharValues.End);
107+
var f1 = runElements.FindIndex(f => f is FieldChar fc && fc.FieldCharType?.Value == FieldCharValues.Begin);
108+
var f2 = runElements.FindIndex(f => f is FieldCode fc && fc.Text.Contains("TOC"));
109+
var f3 = runElements.FindIndex(f => f is FieldChar fc && fc.FieldCharType?.Value == FieldCharValues.Separate);
110+
var f4 = runElements.FindIndex(f => f is FieldChar fc && fc.FieldCharType?.Value == FieldCharValues.End);
106111

107112
if (f1 != -1 && f2 != -1 && f3 != -1 && f2 > f1 && f3 > f2)
108113
{
@@ -111,7 +116,8 @@ private static bool FindToc(Body body, out int ifirst, out int iLast, out string
111116
throw new Exception("Found start of TOC and then another start of TOC");
112117
}
113118

114-
ifirst = i; instr = (runElements[f2] as FieldCode).Text;
119+
ifirst = i;
120+
instr = ((FieldCode) runElements[f2]).Text;
115121
}
116122
if (f4 != -1 && f4 > f1 && f4 > f2 && f4 > f3)
117123
{
@@ -141,7 +147,7 @@ private static bool FindToc(Body body, out int ifirst, out int iLast, out string
141147
continue;
142148
}
143149

144-
var sp = p.ParagraphProperties.OfType<SectionProperties>().FirstOrDefault();
150+
var sp = p.ParagraphProperties?.OfType<SectionProperties>().FirstOrDefault();
145151
if (sp == null)
146152
{
147153
continue;

tools/MarkdownConverter/OptionExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace MarkdownConverter;
44

55
internal static class OptionExtensions
66
{
7-
public static T Option<T>(this FSharpOption<T> o) where T : class
7+
public static T? Option<T>(this FSharpOption<T> o) where T : class
88
{
99
if (FSharpOption<T>.GetTag(o) == FSharpOption<T>.Tags.None)
1010
{

tools/MarkdownConverter/Program.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ static int Main(string[] args)
3636
else
3737
{
3838
// Windows command-shell doesn't do globbing, so we have to do it ourselves
39-
string dir = Path.GetDirectoryName(arg), filename = Path.GetFileName(arg);
39+
string dir = Path.GetDirectoryName(arg) ?? throw new ArgumentException($"'{arg}' is a root directory");
40+
string filename = Path.GetFileName(arg);
4041
if (dir.Contains("*") || dir.Contains("?"))
4142
{
4243
Console.Error.WriteLine("Can't match wildcard directory names");
@@ -55,7 +56,8 @@ static int Main(string[] args)
5556
}
5657

5758
var imdfiles = new List<string>();
58-
string idocxfile = null, odocfile = null;
59+
string? idocxfile = null;
60+
string? odocfile = null;
5961
foreach (var ifile in ifiles)
6062
{
6163
var name = Path.GetFileName(ifile);
@@ -126,7 +128,7 @@ static int Main(string[] args)
126128
Console.WriteLine($"Writing '{Path.GetFileName(odocfile2)}'");
127129
try
128130
{
129-
MarkdownSpecConverter.ConvertToWord(md, idocxfile, odocfile2, reporter);
131+
MarkdownSpecConverter.ConvertToWord(md, idocxfile!, odocfile2, reporter);
130132
}
131133
catch (Exception ex)
132134
{
@@ -142,4 +144,4 @@ static int Main(string[] args)
142144
Console.WriteLine($"Warnings: {reporter.Warnings}");
143145
return reporter.Errors == 0 ? 0 : 1;
144146
}
145-
}
147+
}

tools/MarkdownConverter/Spec/MarkdownSpec.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ private MarkdownSpec(IEnumerable<Tuple<string, MarkdownDocument>> sources, Repor
5555
}
5656
}
5757

58-
public static MarkdownSpec ReadFiles(IEnumerable<string> files, Reporter reporter, Func<string, TextReader> readerProvider = null)
58+
public static MarkdownSpec ReadFiles(IEnumerable<string> files, Reporter reporter, Func<string, TextReader>? readerProvider = null)
5959
{
60-
if (files is null) throw new ArgumentNullException(nameof(files));
60+
if (files is null)
61+
{
62+
throw new ArgumentNullException(nameof(files));
63+
}
6164

6265
readerProvider ??= File.OpenText;
6366

tools/MarkdownConverter/Spec/Reporter.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ public class Reporter
1818
/// <summary>
1919
/// The parent reporter, if any. (This is to allow a complete error/warning count to be kept.)
2020
/// </summary>
21-
private readonly Reporter parent;
21+
private readonly Reporter? parent;
2222

2323
public int Errors { get; private set; }
2424
public int Warnings { get; private set; }
2525

2626
public SourceLocation Location { get; set; } = new SourceLocation(null, null, null, null);
2727

28-
private Reporter(Reporter parent, TextWriter writer, string filename)
28+
private Reporter(Reporter? parent, TextWriter writer, string? filename)
2929
{
3030
this.parent = parent;
3131
this.writer = writer;
@@ -38,33 +38,33 @@ public Reporter(TextWriter writer) : this(null, writer, null)
3838

3939
public Reporter WithFileName(string filename) => new Reporter(this, writer, filename);
4040

41-
public string CurrentFile => Location.File;
41+
public string? CurrentFile => Location.File;
4242

43-
public SectionRef CurrentSection
43+
public SectionRef? CurrentSection
4444
{
4545
get => Location.Section;
4646
set => Location = new SourceLocation(CurrentFile, value, CurrentParagraph, null);
4747
}
4848

49-
public MarkdownParagraph CurrentParagraph
49+
public MarkdownParagraph? CurrentParagraph
5050
{
5151
get => Location.Paragraph;
5252
set => Location = new SourceLocation(CurrentFile, CurrentSection, value, null);
5353
}
5454

55-
public MarkdownSpan CurrentSpan
55+
public MarkdownSpan? CurrentSpan
5656
{
5757
get => Location.Span;
5858
set => Location = new SourceLocation(CurrentFile, CurrentSection, CurrentParagraph, value);
5959
}
6060

61-
public void Error(string code, string msg, SourceLocation loc = null)
61+
public void Error(string code, string msg, SourceLocation? loc = null)
6262
{
6363
IncrementErrors();
6464
Report(code, "ERROR", msg, loc?.Description ?? Location.Description);
6565
}
6666

67-
public void Warning(string code, string msg, SourceLocation loc = null)
67+
public void Warning(string code, string msg, SourceLocation? loc = null)
6868
{
6969
IncrementWarnings();
7070
Report(code, "WARNING", msg, loc?.Description ?? Location.Description);

tools/MarkdownConverter/Spec/SectionRef.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class SectionRef
77
/// <summary>
88
/// Section number, e.g. 10.1.2, or A.3 or null for sections without a number (e.g. Foreword).
99
/// </summary>
10-
public string Number { get; }
10+
public string? Number { get; }
1111

1212
/// <summary>
1313
/// Section title, e.g. "10.1.2 Goto Statement"
@@ -43,9 +43,9 @@ public SectionRef(MarkdownParagraph.Heading mdh, string filename, string bookmar
4343
{
4444
Level = mdh.size;
4545
var spans = mdh.body;
46-
if (spans.Length == 1 && spans.First().IsLiteral)
46+
if (spans.Length == 1 && spans.First() is MarkdownSpan.Literal literal)
4747
{
48-
Title = MarkdownUtilities.UnescapeLiteral(spans.First() as MarkdownSpan.Literal).Trim();
48+
Title = MarkdownUtilities.UnescapeLiteral(literal).Trim();
4949
if (char.IsDigit(Title[0]) || (Title[0] >= 'A' && Title[0] <= 'D' && Title[1] == '.'))
5050
{
5151
var titleParts = Title.Split(new[] { ' ' }, 2);

tools/MarkdownConverter/Spec/SourceLocation.cs

+10-8
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ namespace MarkdownConverter.Spec;
66

77
public class SourceLocation
88
{
9-
public string File { get; }
10-
public SectionRef Section { get; }
11-
public MarkdownParagraph Paragraph { get; }
12-
public MarkdownSpan Span { get; }
13-
public string _loc; // generated lazily.
9+
public string? File { get; }
10+
public SectionRef? Section { get; }
11+
public MarkdownParagraph? Paragraph { get; }
12+
public MarkdownSpan? Span { get; }
13+
public string? _loc; // generated lazily.
1414

15-
public SourceLocation(string file, SectionRef section, MarkdownParagraph paragraph, MarkdownSpan span)
15+
public SourceLocation(string? file, SectionRef? section, MarkdownParagraph? paragraph, MarkdownSpan? span)
1616
{
1717
File = file;
1818
Section = section;
@@ -42,19 +42,21 @@ public string Description
4242
}
4343
else
4444
{
45+
// TODO: Revisit all of the null-forgiving operator usage here at some point.
46+
4547
// Note: we now use the F# Markdown support for ranges, rather than finding text directly.
4648
// This produces slightly weaker diagnostics than before, but it avoids an awful lot of fiddly fuzzy text matching code.
4749

4850
// TODO: Revisit SectionRef.Loc, possibly just exposing the paragraph directly.
49-
var maybeRange = GetRange(Span);
51+
var maybeRange = GetRange(Span!);
5052
if (maybeRange != null)
5153
{
5254
var range = maybeRange.Value;
5355
_loc = $"{File}({range.StartLine},{range.StartColumn},{range.EndLine},{range.EndColumn})";
5456
}
5557
else
5658
{
57-
maybeRange = GetRange(Paragraph) ?? GetRange(Section.Loc.Paragraph);
59+
maybeRange = GetRange(Paragraph!) ?? GetRange(Section!.Loc.Paragraph!);
5860
if (maybeRange == null)
5961
{
6062
// We don't have any line or column information. Just report the filename.

tools/MarkdownConverter/Spec/StringLengthComparer.cs

-19
This file was deleted.

0 commit comments

Comments
 (0)