From 02ac4002bbce19273155b355c06c744b671ea84c Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 21 Jun 2024 09:48:23 +0700 Subject: [PATCH 1/6] Fix nullable reference warnings in AppWritingSystems.cs --- src/SolidGui/AppWritingSystems.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/SolidGui/AppWritingSystems.cs b/src/SolidGui/AppWritingSystems.cs index 8b445133..38b49f5f 100644 --- a/src/SolidGui/AppWritingSystems.cs +++ b/src/SolidGui/AppWritingSystems.cs @@ -10,14 +10,8 @@ namespace SolidGui { public class AppWritingSystems { - private static IWritingSystemRepository _sWritingSystemsRepository; + private static Lazy _sWritingSystemsRepository = new(() => GlobalWritingSystemRepository.Initialize()); - public static IWritingSystemRepository WritingSystems - { - get - { - return _sWritingSystemsRepository ?? (_sWritingSystemsRepository = GlobalWritingSystemRepository.Initialize()); - } - } + public static IWritingSystemRepository WritingSystems => _sWritingSystemsRepository.Value; } } \ No newline at end of file From 758aaafe228c3dabcf47bc1f8845a6043206573b Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 21 Jun 2024 10:23:47 +0700 Subject: [PATCH 2/6] Fix nullable warnings in MainWindowPM This only works in .NET 5.0 or later, though, so for net461 we'll need a different approach. --- src/SolidGui/MainWindowPM.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/SolidGui/MainWindowPM.cs b/src/SolidGui/MainWindowPM.cs index 7b6ec6f8..21250fad 100644 --- a/src/SolidGui/MainWindowPM.cs +++ b/src/SolidGui/MainWindowPM.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Text; @@ -70,6 +71,24 @@ private static String TempDictionaryPath() return Path.Combine(Path.GetTempPath(), "TempDictionary.db"); } + // Modern C# (since .NET 6) warns about constructors not setting all fields, but isn't smart + // enough to know that this method sets those fields. So we have to help its analysis with the + // MemberNotNull] attribute, which informs the compiler that this method will set those members. + // Unfortunately, MemberNotNull is only available in .NET 5.0, so we also have to use an #if. +#if NET5_0_OR_GREATER + [MemberNotNull(nameof(_recordFilters))] + [MemberNotNull(nameof(_workingDictionary))] + [MemberNotNull(nameof(_markerSettingsModel))] + [MemberNotNull(nameof(_warningFilterChooserModel))] + [MemberNotNull(nameof(_navigatorModel))] + [MemberNotNull(nameof(_sfmEditorModel))] + [MemberNotNull(nameof(_searchModel))] + [MemberNotNull(nameof(_cleanUpIndents))] + [MemberNotNull(nameof(_cleanUpClosers))] + [MemberNotNull(nameof(_cleanUpInferred))] + [MemberNotNull(nameof(_cleanUpSeparators))] + [MemberNotNull(nameof(_cleanUpNewlines))] +#endif public void Initialize(SfmDictionary dict) // , SolidSettings settings) { _recordFilters = new RecordFilterSet(); From a0a84b17e50a2a00d897d77a5941f7112f2c21c6 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 21 Jun 2024 10:24:21 +0700 Subject: [PATCH 3/6] Revert "Fix nullable warnings in MainWindowPM" This reverts commit 758aaafe228c3dabcf47bc1f8845a6043206573b. --- src/SolidGui/MainWindowPM.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/SolidGui/MainWindowPM.cs b/src/SolidGui/MainWindowPM.cs index 21250fad..7b6ec6f8 100644 --- a/src/SolidGui/MainWindowPM.cs +++ b/src/SolidGui/MainWindowPM.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Text; @@ -71,24 +70,6 @@ private static String TempDictionaryPath() return Path.Combine(Path.GetTempPath(), "TempDictionary.db"); } - // Modern C# (since .NET 6) warns about constructors not setting all fields, but isn't smart - // enough to know that this method sets those fields. So we have to help its analysis with the - // MemberNotNull] attribute, which informs the compiler that this method will set those members. - // Unfortunately, MemberNotNull is only available in .NET 5.0, so we also have to use an #if. -#if NET5_0_OR_GREATER - [MemberNotNull(nameof(_recordFilters))] - [MemberNotNull(nameof(_workingDictionary))] - [MemberNotNull(nameof(_markerSettingsModel))] - [MemberNotNull(nameof(_warningFilterChooserModel))] - [MemberNotNull(nameof(_navigatorModel))] - [MemberNotNull(nameof(_sfmEditorModel))] - [MemberNotNull(nameof(_searchModel))] - [MemberNotNull(nameof(_cleanUpIndents))] - [MemberNotNull(nameof(_cleanUpClosers))] - [MemberNotNull(nameof(_cleanUpInferred))] - [MemberNotNull(nameof(_cleanUpSeparators))] - [MemberNotNull(nameof(_cleanUpNewlines))] -#endif public void Initialize(SfmDictionary dict) // , SolidSettings settings) { _recordFilters = new RecordFilterSet(); From 8bd43d4139e8dacac4e09f7939bf2fc63c143f6a Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 21 Jun 2024 10:28:49 +0700 Subject: [PATCH 4/6] Fix many nullable warnings in MainWindowPM.cs The pattern where the constructor calls an Initialize() method is one that the C# compiler can't analyze to detect that nullable reference types are actually assigned a real value. So we move most of the code into the constructor (and the static fields are initializes in a static constructor) so that the compiler will stop warning us about them. This still leaves a few compiler warnings that are real, such as the one about _realDictionaryPath, which could indeed be accessed before it is initialized. That one, and a few others, should truly be typed nullable. --- src/SolidGui/MainWindowPM.cs | 45 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/SolidGui/MainWindowPM.cs b/src/SolidGui/MainWindowPM.cs index 7b6ec6f8..8e40e677 100644 --- a/src/SolidGui/MainWindowPM.cs +++ b/src/SolidGui/MainWindowPM.cs @@ -52,29 +52,21 @@ public class MainWindowPM :IDisposable private static Regex _cleanUpNewlinesNonLinux; */ - - public MainWindowPM() - { - Initialize(new SfmDictionary()); // , new SolidSettings()); - } - - public override string ToString() + static MainWindowPM() { - return string.Format("{0} {1} {2} {3} {4} {5} {6}", - _workingDictionary, _markerSettingsModel, _recordFilters, _warningFilterChooserModel, - _navigatorModel, _sfmEditorModel, GetHashCode()); - } - - private static String TempDictionaryPath() - { - return Path.Combine(Path.GetTempPath(), "TempDictionary.db"); + RegexOptions options = RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Multiline; + _cleanUpIndents = new Regex(@"^[ \t]+\\", options); //any line-initial spaces or tabs + _cleanUpClosers = new Regex(@" ?\\\w+\* ?", options); //any closing tag, and possibly one space buffer around it + _cleanUpInferred = new Regex(@"^[ \t]*\\\+.*(\r|\r?\n)", options); //any inferred marker (\+mrkr) and its value and newline (any leading whitespace) + _cleanUpSeparators = new Regex(@"\t", options); // one tab + _cleanUpNewlines = new Regex(@"(\r?\n|\r)", options); // one newline (\r\n or \n or \r) } - public void Initialize(SfmDictionary dict) // , SolidSettings settings) + public MainWindowPM() { _recordFilters = new RecordFilterSet(); - _workingDictionary = dict; - // Settings = settings; + _workingDictionary = new SfmDictionary(); + // Settings = new SolidSettings(); _markerSettingsModel = new MarkerSettingsPM(this); _warningFilterChooserModel = new FilterChooserPM(); _navigatorModel = new RecordNavigatorPM(this); @@ -95,13 +87,18 @@ public void Initialize(SfmDictionary dict) // , SolidSettings settings) // and the choosers should listen to the nav so they can clear themselves as needed NavigatorModel.NavFilterChanged += WarningFilterChooserModel.OnNavFilterChanged; NavigatorModel.NavFilterChanged += MarkerSettingsModel.OnNavFilterChanged; + } - RegexOptions options = RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Multiline; - _cleanUpIndents = new Regex(@"^[ \t]+\\", options); //any line-initial spaces or tabs - _cleanUpClosers = new Regex(@" ?\\\w+\* ?", options); //any closing tag, and possibly one space buffer around it - _cleanUpInferred = new Regex(@"^[ \t]*\\\+.*(\r|\r?\n)", options); //any inferred marker (\+mrkr) and its value and newline (any leading whitespace) - _cleanUpSeparators = new Regex(@"\t", options); // one tab - _cleanUpNewlines = new Regex(@"(\r?\n|\r)", options); // one newline (\r\n or \n or \r) + public override string ToString() + { + return string.Format("{0} {1} {2} {3} {4} {5} {6}", + _workingDictionary, _markerSettingsModel, _recordFilters, _warningFilterChooserModel, + _navigatorModel, _sfmEditorModel, GetHashCode()); + } + + private static String TempDictionaryPath() + { + return Path.Combine(Path.GetTempPath(), "TempDictionary.db"); } public MarkerSettingsPM MarkerSettingsModel From 47c6e7322dce65657a6483516e8d22fe33793c37 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 21 Jun 2024 13:02:02 +0700 Subject: [PATCH 5/6] Get rid of some more nullable warnings Not all nullable warnings will be able to be removed as long as this code still targets net461; for example, string.IsNullOrEmpty guarantees that the string isn't null, but the annotations required for the compiler to know that weren't added until the .NET Core era. So until the projects targets a modern version of .NET, the only way to get rid of nullable references like these is to use the ! operator every time, which will quickly get tedious. --- src/SolidGui/DataShapesDialog.cs | 8 ++++---- src/SolidGui/DataValuesDialog.cs | 3 ++- src/SolidGui/QuickFixer.cs | 14 +++++++------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/SolidGui/DataShapesDialog.cs b/src/SolidGui/DataShapesDialog.cs index 704f8643..cdfdb28f 100644 --- a/src/SolidGui/DataShapesDialog.cs +++ b/src/SolidGui/DataShapesDialog.cs @@ -18,7 +18,7 @@ namespace SolidGui { public partial class DataShapesDialog : Form { - private FindReplaceDialog _searchDialog; + private FindReplaceDialog? _searchDialog; public DataShapesDialog(FindReplaceDialog searchDialog, MainWindowPM mwp) { @@ -34,9 +34,9 @@ public DataShapesDialog(FindReplaceDialog searchDialog, MainWindowPM mwp) _mainWindowPm = mwp; } - private MainWindowPM _mainWindowPm; + private MainWindowPM? _mainWindowPm; - private IEnumerable _shapes; + private IEnumerable? _shapes; private void _closeButton_Click(object sender, EventArgs e) { @@ -166,7 +166,7 @@ except that dollars ($1$2) would need to be backslashes (\1\2). if any of the fields have hard-wrapped data."; RegexItem r = RegexItem.GetCustomRegex(sbFind.ToString(), sbReplace.ToString(), help, true); - _searchDialog.LaunchSearch(r); + _searchDialog?.LaunchSearch(r); } diff --git a/src/SolidGui/DataValuesDialog.cs b/src/SolidGui/DataValuesDialog.cs index c4ba9062..554a5336 100644 --- a/src/SolidGui/DataValuesDialog.cs +++ b/src/SolidGui/DataValuesDialog.cs @@ -20,7 +20,7 @@ public DataValuesDialog(MainWindowPM mwp) _mainWindowPm = mwp; } - private MainWindowPM _mainWindowPm; + private MainWindowPM? _mainWindowPm; private void _runButton_Click(object sender, EventArgs e) { @@ -30,6 +30,7 @@ private void _runButton_Click(object sender, EventArgs e) private void Run() { if (String.IsNullOrEmpty(_markersTextBox.Text.Trim())) return; + if (_mainWindowPm == null) return; int max = (int)maxNumericUpDown.Value; diff --git a/src/SolidGui/QuickFixer.cs b/src/SolidGui/QuickFixer.cs index bb396f0f..5e0ebd8c 100644 --- a/src/SolidGui/QuickFixer.cs +++ b/src/SolidGui/QuickFixer.cs @@ -187,7 +187,7 @@ private void PropagateField(string markerOfFieldToPropagate, string markerOfFiel { foreach (Record record in _dictionary.Records) { - SfmFieldModel fieldToCopy = null; + SfmFieldModel? fieldToCopy = null; for (int i = 0; i < record.Fields.Count; i++) { SfmFieldModel field = record.Fields[i]; @@ -244,13 +244,13 @@ private void AddNewEntries(List additions, StringBuilder log) SolidSettings nullSettings = new SolidSettings(); // JMC: why a new bunch? foreach (RecordAddition addition in additions) { - string switchToCitationForm; - Record targetRecord = FindRecordByCitationFormOrLexemeForm(addition.targetHeadWord, out switchToCitationForm); - if (targetRecord == null) + string? switchToCitationForm; + Record? targetRecord = FindRecordByCitationFormOrLexemeForm(addition.targetHeadWord, out switchToCitationForm); + if (targetRecord is null) { targetRecord = FindRecordContainingVariantOrSubEntry(addition.targetHeadWord); } - if (null == targetRecord) + if (targetRecord is null) { Record r = new Record(); var b = new StringBuilder(); @@ -309,7 +309,7 @@ private void SplitFieldsWithMultipleItems(List markers, StringBuilder lo } - private Record FindRecordByCitationFormOrLexemeForm(string form, out string switchToCitationForm) + private Record? FindRecordByCitationFormOrLexemeForm(string form, out string? switchToCitationForm) { switchToCitationForm = null; form = form.Trim(); @@ -348,7 +348,7 @@ private Record FindRecordByCitationFormOrLexemeForm(string form, out string swit return null; } - private Record FindRecordContainingVariantOrSubEntry(string form) + private Record? FindRecordContainingVariantOrSubEntry(string form) { foreach (Record record in _dictionary.Records) From ecd4c498e02d5ba5b1e2b0fe60a6b2d9ae31695d Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 21 Jun 2024 15:28:58 +0700 Subject: [PATCH 6/6] A few more null reference errors worth fixing --- src/SolidGui/Engine/SfmField.cs | 2 +- src/SolidGui/Engine/SolidReport.cs | 1 + src/SolidGui/Engine/SolidSettings.cs | 3 ++- src/SolidGui/MainWindowPM.cs | 2 +- src/SolidGui/Processes/ProcessStructure.cs | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/SolidGui/Engine/SfmField.cs b/src/SolidGui/Engine/SfmField.cs index c8ece467..cc115e07 100644 --- a/src/SolidGui/Engine/SfmField.cs +++ b/src/SolidGui/Engine/SfmField.cs @@ -30,7 +30,7 @@ public SfmField() public string Trailing // (for trailing white space); added -JMC 2013-09 { get { return String.IsNullOrEmpty(_trailing) ? DefaultTrailing : _trailing; } - set { _trailing = value.Contains("\n") ? value : null; } + set { _trailing = value.Contains("\n") ? value : ""; } } // Set both the value and the trailing-space value using a single string diff --git a/src/SolidGui/Engine/SolidReport.cs b/src/SolidGui/Engine/SolidReport.cs index 443fb66c..82c91893 100644 --- a/src/SolidGui/Engine/SolidReport.cs +++ b/src/SolidGui/Engine/SolidReport.cs @@ -43,6 +43,7 @@ public static bool IsDataWarning (EntryType et) public SolidReport() { Entries = new List(); + FilePath = ""; } public static SolidReport MakeCopy(SolidReport report) diff --git a/src/SolidGui/Engine/SolidSettings.cs b/src/SolidGui/Engine/SolidSettings.cs index ea823b77..07822dd5 100644 --- a/src/SolidGui/Engine/SolidSettings.cs +++ b/src/SolidGui/Engine/SolidSettings.cs @@ -33,6 +33,7 @@ public SolidSettings(List ms) _markerSettings = ms; _newlyAdded = new List(); FileStatusReport = new SettingsFileReport(); + FilePath = ""; } public SolidSettings() @@ -307,7 +308,7 @@ public static SolidSettings CreateSolidFileFromTemplate(string templateFilePath, { ErrorReport.NotifyUserOfProblem( "There was a problem opening that settings file. The error was\r\n" + e.Message); - return null; + return new SolidSettings(); // Don't want to return null, so return default settings } return OpenSolidFile(outputFilePath); diff --git a/src/SolidGui/MainWindowPM.cs b/src/SolidGui/MainWindowPM.cs index 8e40e677..d6d04ac3 100644 --- a/src/SolidGui/MainWindowPM.cs +++ b/src/SolidGui/MainWindowPM.cs @@ -333,7 +333,7 @@ public bool ShouldAskForTemplateBeforeOpening(string filePath) // Check also that the setting file is valid. If it's not allow true to be returned to pop up // the template chooser. // See http://projects.palaso.org/issues/show/180 - SolidSettings solidSettings = null; + SolidSettings? solidSettings = null; try { solidSettings = LoadSettingsFromExistingFile(solidFilePath); // A dry run. If it succeeds, we'll reload later. -JMC diff --git a/src/SolidGui/Processes/ProcessStructure.cs b/src/SolidGui/Processes/ProcessStructure.cs index 4677ada8..61eea7c8 100644 --- a/src/SolidGui/Processes/ProcessStructure.cs +++ b/src/SolidGui/Processes/ProcessStructure.cs @@ -321,7 +321,7 @@ private static void AddClosers(SfmLexEntry lexEntry) */ - private static SfmFieldModel _appendTo; + private static SfmFieldModel? _appendTo; // Recursive private static void AddClosers(SfmFieldModel node)