From b0c8b7b2e53ee2a069eece93a8984f06ac1878b0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 20 Jun 2026 11:27:34 +0200 Subject: [PATCH] Enable nullable reference types across the remaining C# Syntax files Completes the nullable migration for ICSharpCode.Decompiler/CSharp/Syntax: every hand-written file now carries #nullable enable. The pattern-matching API (INode/Pattern/PatternExtensions DoMatch/Match/IsMatch) takes a nullable 'other', since matching legitimately compares a pattern against a missing child. Members that genuinely return or accept null are annotated to match (AttributeSection's target token, IAnnotatable.Annotation, Statement/Expression.ReplaceWith, SyntaxExtensions.GetNextStatement), and a latent null dereference in Backreference.DoMatch is fixed. Assisted-by: Claude:claude-opus-4-8:Claude Code --- .../CSharp/StatementBuilder.cs | 4 +- .../CSharp/Syntax/AstType.cs | 4 +- .../CSharp/Syntax/DepthFirstAstVisitor.cs | 10 ++++- .../Expressions/ArrayInitializerExpression.cs | 2 + .../Expressions/DecompilerAstNodeAttribute.cs | 2 + .../CSharp/Syntax/Expressions/Expression.cs | 6 ++- .../Syntax/GeneralScope/AttributeSection.cs | 6 ++- .../CSharp/Syntax/GeneralScope/Trivia.cs | 2 + .../CSharp/Syntax/IAnnotatable.cs | 44 ++++++++++--------- .../CSharp/Syntax/Modifiers.cs | 2 + .../CSharp/Syntax/PatternMatching/AnyNode.cs | 10 +++-- .../Syntax/PatternMatching/AnyNodeOrNull.cs | 10 +++-- .../Syntax/PatternMatching/Backreference.cs | 8 ++-- .../PatternMatching/BacktrackingInfo.cs | 2 + .../CSharp/Syntax/PatternMatching/Choice.cs | 4 +- .../CSharp/Syntax/PatternMatching/INode.cs | 8 ++-- .../PatternMatching/IPatternPlaceholder.cs | 2 + .../IdentifierExpressionBackreference.cs | 4 +- .../CSharp/Syntax/PatternMatching/Match.cs | 18 ++++---- .../Syntax/PatternMatching/NamedNode.cs | 4 +- .../Syntax/PatternMatching/OptionalNode.cs | 4 +- .../CSharp/Syntax/PatternMatching/Pattern.cs | 6 ++- .../CSharp/Syntax/PatternMatching/Repeat.cs | 4 +- ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs | 2 + .../Syntax/Statements/BlockStatement.cs | 2 + .../CSharp/Syntax/Statements/Statement.cs | 6 ++- .../CSharp/Syntax/SyntaxExtensions.cs | 10 +++-- .../CSharp/Syntax/TextLocation.cs | 12 ++--- .../Syntax/TypeMembers/EntityDeclaration.cs | 2 + .../Transforms/CombineQueryExpressions.cs | 10 ++--- .../CSharp/Transforms/CustomPatterns.cs | 6 +-- .../Transforms/IntroduceExtensionMethods.cs | 9 ++-- .../Transforms/IntroduceQueryExpressions.cs | 6 ++- .../Transforms/IntroduceUsingDeclarations.cs | 2 +- .../Transforms/PatternStatementTransform.cs | 16 ++++--- 35 files changed, 160 insertions(+), 89 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 6e78b73f5..da0646022 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -764,8 +764,8 @@ namespace ICSharpCode.Decompiler.CSharp Statement firstStatement = foreachBody.Statements.First(); if (firstStatement is LabelStatement) { - // skip the entry-point label, if any - firstStatement = firstStatement.GetNextStatement(); + // skip the entry-point label, if any; the assignment statement always follows it + firstStatement = firstStatement.GetNextStatement()!; } Debug.Assert(firstStatement is ExpressionStatement); firstStatement.Remove(); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs index 8a0537dff..5ff696233 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System.Collections.Generic; using ICSharpCode.Decompiler.CSharp.Resolver; @@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public bool IsVar() { - SimpleType st = this as SimpleType; + SimpleType? st = this as SimpleType; return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs index c1e293499..432eac46f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -711,7 +713,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { child.AcceptVisitor(this); } - return default(T); + // Unconstrained T: the default-returning visitor methods are the no-op base behavior + // for node kinds a derived visitor does not override. + return default(T)!; } public virtual T VisitSyntaxTree(SyntaxTree unit) @@ -1384,7 +1388,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { child.AcceptVisitor(this, data); } - return default(S); + // Unconstrained S: the default-returning visitor methods are the no-op base behavior + // for node kinds a derived visitor does not override. + return default(S)!; } public virtual S VisitSyntaxTree(SyntaxTree unit, T data) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayInitializerExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayInitializerExpression.cs index e1fc0be05..3d1ea027b 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayInitializerExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayInitializerExpression.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using System.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DecompilerAstNodeAttribute.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DecompilerAstNodeAttribute.cs index a0dd2b2ae..b4ad8ca31 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DecompilerAstNodeAttribute.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DecompilerAstNodeAttribute.cs @@ -22,6 +22,8 @@ +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { class DecompilerAstNodeAttribute : System.Attribute diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs index ca8316ac6..9279b722f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -35,11 +37,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return (Expression)base.Clone(); } - public Expression ReplaceWith(Func replaceFunction) + public Expression? ReplaceWith(Func replaceFunction) { if (replaceFunction == null) throw new ArgumentNullException(nameof(replaceFunction)); - return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); + return (Expression?)base.ReplaceWith(node => replaceFunction((Expression)node)); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs index a9b63bd25..0dd95cf65 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -42,9 +44,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } // DoMatch compares the name string; exclude the token slot to avoid matching it twice. + // Optional: most attribute sections have no target (e.g. [Foo]); only [return:]/[assembly:]/... + // carry one, so the backing token slot may be empty. [ExcludeFromMatch] [Slot("Identifier")] - public partial Identifier AttributeTargetToken { get; set; } + public partial Identifier? AttributeTargetToken { get; set; } [Slot("Attribute")] public partial AstNodeCollection Attributes { get; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Trivia.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Trivia.cs index 7c43b47d3..dbd8ee9d4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Trivia.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Trivia.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs index 0dba3d644..e8e2ad907 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -43,7 +45,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The type of the annotation. /// - T Annotation() where T : class; + T? Annotation() where T : class; /// /// Gets the first annotation of the specified type. @@ -52,7 +54,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The type of the annotation. /// - object Annotation(Type type); + object? Annotation(Type type); /// /// Adds an annotation to this instance. @@ -90,7 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax // or to an AnnotationList. // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list) - object annotations; + object? annotations; /// /// Clones all annotations. @@ -102,7 +104,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// protected void CloneAnnotations() { - ICloneable cloneable = annotations as ICloneable; + ICloneable? cloneable = annotations as ICloneable; if (cloneable != null) annotations = cloneable.Clone(); } @@ -126,8 +128,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax for (int i = 0; i < this.Count; i++) { object obj = this[i]; - ICloneable c = obj as ICloneable; - copy.Add(c != null ? c.Clone() : obj); + ICloneable? c = obj as ICloneable; + copy.Add(c != null ? c.Clone()! : obj); } return copy; } @@ -139,12 +141,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax if (annotation == null) throw new ArgumentNullException(nameof(annotation)); retry: // Retry until successful - object oldAnnotation = Interlocked.CompareExchange(ref this.annotations, annotation, null); + object? oldAnnotation = Interlocked.CompareExchange(ref this.annotations, annotation, null); if (oldAnnotation == null) { return; // we successfully added a single annotation } - AnnotationList list = oldAnnotation as AnnotationList; + AnnotationList? list = oldAnnotation as AnnotationList; if (list == null) { // we need to transform the old annotation into a list @@ -170,8 +172,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public virtual void RemoveAnnotations() where T : class { retry: // Retry until successful - object oldAnnotations = this.annotations; - AnnotationList list = oldAnnotations as AnnotationList; + object? oldAnnotations = this.annotations; + AnnotationList? list = oldAnnotations as AnnotationList; if (list != null) { lock (list) @@ -192,8 +194,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax if (type == null) throw new ArgumentNullException(nameof(type)); retry: // Retry until successful - object oldAnnotations = this.annotations; - AnnotationList list = oldAnnotations as AnnotationList; + object? oldAnnotations = this.annotations; + AnnotationList? list = oldAnnotations as AnnotationList; if (list != null) { lock (list) @@ -209,17 +211,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } - public T Annotation() where T : class + public T? Annotation() where T : class { - object annotations = this.annotations; - AnnotationList list = annotations as AnnotationList; + object? annotations = this.annotations; + AnnotationList? list = annotations as AnnotationList; if (list != null) { lock (list) { foreach (object obj in list) { - T t = obj as T; + T? t = obj as T; if (t != null) return t; } @@ -232,12 +234,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } - public object Annotation(Type type) + public object? Annotation(Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); - object annotations = this.annotations; - AnnotationList list = annotations as AnnotationList; + object? annotations = this.annotations; + AnnotationList? list = annotations as AnnotationList; if (list != null) { lock (list) @@ -262,8 +264,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public IEnumerable Annotations { get { - object annotations = this.annotations; - AnnotationList list = annotations as AnnotationList; + object? annotations = this.annotations; + AnnotationList? list = annotations as AnnotationList; if (list != null) { lock (list) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs index b93987a7d..d286f21cf 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs @@ -26,6 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +#nullable enable + using System; using System.Collections.Immutable; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs index 85993d6d4..d828f02fb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// @@ -24,18 +26,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching /// Does not match null nodes. public class AnyNode : Pattern { - readonly string groupName; + readonly string? groupName; - public string GroupName { + public string? GroupName { get { return groupName; } } - public AnyNode(string groupName = null) + public AnyNode(string? groupName = null) { this.groupName = groupName; } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { match.Add(this.groupName, other); return other != null; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs index a7fe085c6..e62e2dd21 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// @@ -32,18 +34,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching /// Does not match null nodes. public class AnyNodeOrNull : Pattern { - readonly string groupName; + readonly string? groupName; - public string GroupName { + public string? GroupName { get { return groupName; } } - public AnyNodeOrNull(string groupName = null) + public AnyNodeOrNull(string? groupName = null) { this.groupName = groupName; } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { if (other == null) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs index 9be8cf6f4..5d3ee2471 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Linq; @@ -39,11 +41,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching this.referencedGroupName = referencedGroupName; } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { var last = match.Get(referencedGroupName).LastOrDefault(); - if (last == null && other == null) - return true; + if (last == null) + return other == null; return last.IsMatch(other); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/BacktrackingInfo.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/BacktrackingInfo.cs index 2d03e1ed4..1f37fd136 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/BacktrackingInfo.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/BacktrackingInfo.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs index 6b87eb1bd..6eaa059d5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections; using System.Collections.Generic; @@ -43,7 +45,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching alternatives.Add(alternative); } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { var checkPoint = match.CheckPoint(); foreach (INode alt in alternatives) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs index acdb9b425..d4d1133d7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections.Generic; @@ -26,7 +28,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching /// public interface INode { - bool DoMatch(INode other, Match match); + bool DoMatch(INode? other, Match match); /// /// Matches this pattern node against the collection element at . @@ -51,7 +53,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching /// However, it is also possible to match two ASTs without any pattern nodes - /// doing so will produce a successful match if the two ASTs are structurally identical. /// - public static Match Match(this INode pattern, INode other) + public static Match Match(this INode pattern, INode? other) { if (pattern == null) throw new ArgumentNullException(nameof(pattern)); @@ -62,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching return default(PatternMatching.Match); } - public static bool IsMatch(this INode pattern, INode other) + public static bool IsMatch(this INode pattern, INode? other) { if (pattern == null) throw new ArgumentNullException(nameof(pattern)); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IPatternPlaceholder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IPatternPlaceholder.cs index 1b8f9e70a..34400b4a2 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IPatternPlaceholder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IPatternPlaceholder.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IdentifierExpressionBackreference.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IdentifierExpressionBackreference.cs index b77661beb..375813004 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IdentifierExpressionBackreference.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IdentifierExpressionBackreference.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Linq; @@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching this.referencedGroupName = referencedGroupName; } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { var ident = other as IdentifierExpression; if (ident == null || ident.TypeArguments.Any()) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs index 0d6dd57e0..464c497fc 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching @@ -27,7 +29,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { // TODO: maybe we should add an implicit Match->bool conversion? (implicit operator bool(Match m) { return m != null; }) - List> results; + List> results; public bool Success { get { return results != null; } @@ -36,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching internal static Match CreateNew() { Match m; - m.results = new List>(); + m.results = new List>(); return m; } @@ -50,7 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching results.RemoveRange(checkPoint, results.Count - checkPoint); } - public IEnumerable Get(string groupName) + public IEnumerable Get(string groupName) { if (results == null) yield break; @@ -68,7 +70,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching foreach (var pair in results) { if (pair.Key == groupName) - yield return (T)pair.Value; + yield return (T)pair.Value!; } } @@ -84,19 +86,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching return false; } - public void Add(string groupName, INode node) + public void Add(string? groupName, INode? node) { if (groupName != null && node != null) { - results.Add(new KeyValuePair(groupName, node)); + results.Add(new KeyValuePair(groupName, node)); } } - internal void AddNull(string groupName) + internal void AddNull(string? groupName) { if (groupName != null) { - results.Add(new KeyValuePair(groupName, null)); + results.Add(new KeyValuePair(groupName, null)); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs index f197b3840..3cce5a96e 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching @@ -44,7 +46,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching this.childNode = childNode; } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { match.Add(this.groupName, other); return childNode.DoMatch(other, match); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs index 8da9f0924..843e7c3e9 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections.Generic; @@ -48,7 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching return childNode.DoMatch(pos < other.Count ? other[pos] : null, match); } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { if (other == null) return true; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs index 071a115f0..160309fa9 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System.Collections.Generic; using System.Diagnostics; @@ -31,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching /// public static readonly string AnyString = "$any$"; - public static bool MatchString(string pattern, string text) + public static bool MatchString(string? pattern, string? text) { return pattern == AnyString || pattern == text; } @@ -48,7 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching } } - public abstract bool DoMatch(INode other, Match match); + public abstract bool DoMatch(INode? other, Match match); public virtual bool DoMatchCollection(IReadOnlyList other, int pos, Match match, BacktrackingInfo backtrackingInfo) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs index a0612f18c..58a1d6db1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections.Generic; @@ -60,7 +62,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack. } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { if (other == null) return this.MinCount <= 0; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs index bf61d66ec..5c4a4ed10 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { public static class Roles diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs index a057b5c49..4643c5676 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using System.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs index c30a0b6ac..1a548b5fa 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -35,11 +37,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return (Statement)base.Clone(); } - public Statement ReplaceWith(Func replaceFunction) + public Statement? ReplaceWith(Func replaceFunction) { if (replaceFunction == null) throw new ArgumentNullException(nameof(replaceFunction)); - return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); + return (Statement?)base.ReplaceWith(node => replaceFunction((Statement)node)); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs index 0fa3c5dc9..daae52821 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs @@ -18,6 +18,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -51,15 +53,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax || operatorType == BinaryOperatorType.ExclusiveOr; } - public static Statement GetNextStatement(this Statement statement) + public static Statement? GetNextStatement(this Statement statement) { - AstNode next = statement.NextSibling; + AstNode? next = statement.NextSibling; while (next != null && !(next is Statement)) next = next.NextSibling; - return (Statement)next; + return (Statement?)next; } - public static bool IsArgList(this AstType type) + public static bool IsArgList(this AstType? type) { var simpleType = type as SimpleType; return simpleType != null && simpleType.Identifier == "__arglist"; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs index e742b76f2..7e6005bd7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.ComponentModel; using System.Globalization; @@ -98,7 +100,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// Equality test. /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (!(obj is TextLocation)) return false; @@ -187,17 +189,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public class TextLocationConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(TextLocation) || base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string) { @@ -210,7 +212,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (value is TextLocation) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs index a48cc8b10..8527a6cdb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System.Collections.Generic; using System.Linq; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs index ccac408b8..49496c1e2 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (!context.Settings.QueryExpressions) return; - CombineQueries(rootNode, new Dictionary()); + CombineQueries(rootNode, new Dictionary()); } static readonly InvocationExpression castPattern = new InvocationExpression { @@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } }; - void CombineQueries(AstNode node, Dictionary fromOrLetIdentifiers) + void CombineQueries(AstNode node, Dictionary fromOrLetIdentifiers) { AstNode? next; for (AstNode? child = node.FirstChild; child != null; child = next) @@ -103,7 +103,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } }; - bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, Dictionary letClauses) + bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, Dictionary letClauses) { if (!CSharpDecompiler.IsTransparentIdentifier(fromClause.Identifier)) return false; @@ -161,7 +161,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// /// Removes all occurrences of transparent identifiers /// - void RemoveTransparentIdentifierReferences(AstNode node, Dictionary fromOrLetIdentifiers) + void RemoveTransparentIdentifierReferences(AstNode node, Dictionary fromOrLetIdentifiers) { foreach (AstNode child in node.Children) { @@ -174,7 +174,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms mre.TypeArguments.MoveTo(newIdent.TypeArguments); newIdent.CopyAnnotationsFrom(mre); newIdent.RemoveAnnotations(); // remove the reference to the property of the anonymous type - if (fromOrLetIdentifiers.TryGetValue(mre.MemberName, out var annotation)) + if (fromOrLetIdentifiers.TryGetValue(mre.MemberName, out var annotation) && annotation != null) newIdent.AddAnnotation(annotation); mre.ReplaceWith(newIdent); return; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs b/ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs index 54d0adeff..e1e4dff2d 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms this.name = type.Name; } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { ComposedType? ct = other as ComposedType; AstType? o; @@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms this.childNode = new AnyNode(groupName); } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { InvocationExpression? ie = other as InvocationExpression; if (ie != null && ie.Annotation() != null && ie.Arguments.Count == 1) @@ -107,7 +107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms ), "TypeHandle"); } - public override bool DoMatch(INode other, Match match) + public override bool DoMatch(INode? other, Match match) { return childNode.DoMatch(other, match); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index 6f88527b4..1535260c3 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -48,7 +48,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { this.context = context; this.conversions = CSharpConversions.Get(context.TypeSystem); - InitializeContext(rootNode.Annotation()); + // The decompiler attaches a UsingScope annotation to the syntax-tree root. + InitializeContext(rootNode.Annotation()!); rootNode.AcceptVisitor(this); } @@ -111,14 +112,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out) return; - firstArgument = dirExpr.Expression; + // A ref/out direction expression always wraps an operand. + firstArgument = dirExpr.Expression!; target = firstArgument.GetResolveResult(); dirExpr.Detach(); } else if (firstArgument is NullReferenceExpression) { Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace)); - firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach())); + // The replacement is a freshly created CastExpression, so the result is non-null. + firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach()))!; } if (invocationExpression.Target is IdentifierExpression identifierExpression) { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index 8fb550b7d..2bd67a7cf 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -168,7 +168,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Projection = elementSelector.Detach(), Key = keySelector.Detach() }; - queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation(), projectionLambda.Annotation())); + // The matched group-by lambdas always carry their ILFunction annotation. + queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation()!, projectionLambda.Annotation()!)); query.Clauses.Add(queryGroupClause); return query; } @@ -314,7 +315,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms joinClause.IntoIdentifier = p2.Name; // into p2.Name joinClause.IntoIdentifierToken!.CopyAnnotationsFrom(p2); } - joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation(), innerLambda.Annotation())); + // The matched join key-selector lambdas always carry their ILFunction annotation. + joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation()!, innerLambda.Annotation()!)); query.Clauses.Add(joinClause); query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }.CopyAnnotationsFrom(lambda)); return query; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs index e4ee9dd7f..132eb6444 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs @@ -379,7 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public override void VisitSimpleType(SimpleType simpleType) { - TypeResolveResult rr; + TypeResolveResult? rr; if ((rr = simpleType.Annotation()) == null) { base.VisitSimpleType(simpleType); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 74eb24407..fcb5549ab 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return declPoint.Ancestors.Contains(loop) && !declareVariables.WasMerged(itemVar); } - static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop) + static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer? loop) { if (v.StoreCount == 1 && v.AddressCount == 1 && v.LoadCount == 0 && v.Type.IsReferenceType == false) { @@ -525,7 +525,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (!MatchForeachOnMultiDimArray(upperBounds, collection, stmt, out var foreachVariable, out var statements, out var lowerBounds)) return null; statementsToDelete.Add(stmt); - statementsToDelete.Add(stmt.GetNextStatement()); + // The matched multi-dimensional foreach pattern guarantees a statement after stmt. + statementsToDelete.Add(stmt.GetNextStatement()!); var itemVariable = foreachVariable.GetILVariable(); if (itemVariable == null || !itemVariable.IsSingleDefinition || (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot) @@ -1098,7 +1099,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms dd.CopyAnnotationsFrom(methodDef); dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override); dd.Body = m.Get("body").Single().Detach(); - dd.Name = currentTypeDefinition?.Name; + // A destructor only appears inside a type declaration, so the context tracker + // has an enclosing type at this point. + dd.Name = currentTypeDefinition!.Name; methodDef.ReplaceWith(dd); return dd; } @@ -1188,9 +1191,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var bAndC = expr.Right as BinaryOperatorExpression; if (bAndC != null && bAndC.Operator == expr.Operator) { - // make bAndC the parent and expr the child - var b = bAndC.Left.Detach(); - var c = bAndC.Right.Detach(); + // make bAndC the parent and expr the child. + // A conditional-and/or operator always has both operands present. + var b = bAndC.Left!.Detach(); + var c = bAndC.Right!.Detach(); expr.ReplaceWith(bAndC.Detach()); bAndC.Left = expr; bAndC.Right = c;