Browse Source

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<T>, Statement/Expression.ReplaceWith,
SyntaxExtensions.GetNextStatement), and a latent null dereference in
Backreference.DoMatch is fixed.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3807/head
Siegfried Pammer 3 weeks ago committed by Siegfried Pammer
parent
commit
b0c8b7b2e5
  1. 4
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs
  3. 10
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayInitializerExpression.cs
  5. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DecompilerAstNodeAttribute.cs
  6. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs
  7. 6
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs
  8. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Trivia.cs
  9. 44
      ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs
  10. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs
  11. 10
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs
  12. 10
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs
  13. 8
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs
  14. 2
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/BacktrackingInfo.cs
  15. 4
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs
  16. 8
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs
  17. 2
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IPatternPlaceholder.cs
  18. 4
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/IdentifierExpressionBackreference.cs
  19. 18
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs
  20. 4
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs
  21. 4
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs
  22. 6
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs
  23. 4
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs
  24. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs
  25. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs
  26. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs
  27. 10
      ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs
  28. 12
      ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs
  29. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs
  30. 10
      ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs
  31. 6
      ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs
  32. 9
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  33. 6
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs
  34. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs
  35. 16
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

4
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -764,8 +764,8 @@ namespace ICSharpCode.Decompiler.CSharp
Statement firstStatement = foreachBody.Statements.First(); Statement firstStatement = foreachBody.Statements.First();
if (firstStatement is LabelStatement) if (firstStatement is LabelStatement)
{ {
// skip the entry-point label, if any // skip the entry-point label, if any; the assignment statement always follows it
firstStatement = firstStatement.GetNextStatement(); firstStatement = firstStatement.GetNextStatement()!;
} }
Debug.Assert(firstStatement is ExpressionStatement); Debug.Assert(firstStatement is ExpressionStatement);
firstStatement.Remove(); firstStatement.Remove();

4
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary> /// </summary>
public bool IsVar() public bool IsVar()
{ {
SimpleType st = this as SimpleType; SimpleType? st = this as SimpleType;
return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0;
} }

10
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>
@ -711,7 +713,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
child.AcceptVisitor(this); 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) public virtual T VisitSyntaxTree(SyntaxTree unit)
@ -1384,7 +1388,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
child.AcceptVisitor(this, data); 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) public virtual S VisitSyntaxTree(SyntaxTree unit, T data)

2
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax

2
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DecompilerAstNodeAttribute.cs

@ -22,6 +22,8 @@
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
class DecompilerAstNodeAttribute : System.Attribute class DecompilerAstNodeAttribute : System.Attribute

6
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
@ -35,11 +37,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return (Expression)base.Clone(); return (Expression)base.Clone();
} }
public Expression ReplaceWith(Func<Expression, Expression> replaceFunction) public Expression? ReplaceWith(Func<Expression, Expression> replaceFunction)
{ {
if (replaceFunction == null) if (replaceFunction == null)
throw new ArgumentNullException(nameof(replaceFunction)); throw new ArgumentNullException(nameof(replaceFunction));
return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); return (Expression?)base.ReplaceWith(node => replaceFunction((Expression)node));
} }
} }
} }

6
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>
@ -42,9 +44,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
// DoMatch compares the name string; exclude the token slot to avoid matching it twice. // 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] [ExcludeFromMatch]
[Slot("Identifier")] [Slot("Identifier")]
public partial Identifier AttributeTargetToken { get; set; } public partial Identifier? AttributeTargetToken { get; set; }
[Slot("Attribute")] [Slot("Attribute")]
public partial AstNodeCollection<Attribute> Attributes { get; } public partial AstNodeCollection<Attribute> Attributes { get; }

2
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>

44
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@ -43,7 +45,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <typeparam name='T'> /// <typeparam name='T'>
/// The type of the annotation. /// The type of the annotation.
/// </typeparam> /// </typeparam>
T Annotation<T>() where T : class; T? Annotation<T>() where T : class;
/// <summary> /// <summary>
/// Gets the first annotation of the specified type. /// Gets the first annotation of the specified type.
@ -52,7 +54,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <param name='type'> /// <param name='type'>
/// The type of the annotation. /// The type of the annotation.
/// </param> /// </param>
object Annotation(Type type); object? Annotation(Type type);
/// <summary> /// <summary>
/// Adds an annotation to this instance. /// Adds an annotation to this instance.
@ -90,7 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
// or to an AnnotationList. // or to an AnnotationList.
// Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list) // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list)
object annotations; object? annotations;
/// <summary> /// <summary>
/// Clones all annotations. /// Clones all annotations.
@ -102,7 +104,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary> /// </summary>
protected void CloneAnnotations() protected void CloneAnnotations()
{ {
ICloneable cloneable = annotations as ICloneable; ICloneable? cloneable = annotations as ICloneable;
if (cloneable != null) if (cloneable != null)
annotations = cloneable.Clone(); annotations = cloneable.Clone();
} }
@ -126,8 +128,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
for (int i = 0; i < this.Count; i++) for (int i = 0; i < this.Count; i++)
{ {
object obj = this[i]; object obj = this[i];
ICloneable c = obj as ICloneable; ICloneable? c = obj as ICloneable;
copy.Add(c != null ? c.Clone() : obj); copy.Add(c != null ? c.Clone()! : obj);
} }
return copy; return copy;
} }
@ -139,12 +141,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (annotation == null) if (annotation == null)
throw new ArgumentNullException(nameof(annotation)); throw new ArgumentNullException(nameof(annotation));
retry: // Retry until successful 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) if (oldAnnotation == null)
{ {
return; // we successfully added a single annotation return; // we successfully added a single annotation
} }
AnnotationList list = oldAnnotation as AnnotationList; AnnotationList? list = oldAnnotation as AnnotationList;
if (list == null) if (list == null)
{ {
// we need to transform the old annotation into a list // we need to transform the old annotation into a list
@ -170,8 +172,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public virtual void RemoveAnnotations<T>() where T : class public virtual void RemoveAnnotations<T>() where T : class
{ {
retry: // Retry until successful retry: // Retry until successful
object oldAnnotations = this.annotations; object? oldAnnotations = this.annotations;
AnnotationList list = oldAnnotations as AnnotationList; AnnotationList? list = oldAnnotations as AnnotationList;
if (list != null) if (list != null)
{ {
lock (list) lock (list)
@ -192,8 +194,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (type == null) if (type == null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
retry: // Retry until successful retry: // Retry until successful
object oldAnnotations = this.annotations; object? oldAnnotations = this.annotations;
AnnotationList list = oldAnnotations as AnnotationList; AnnotationList? list = oldAnnotations as AnnotationList;
if (list != null) if (list != null)
{ {
lock (list) lock (list)
@ -209,17 +211,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
} }
public T Annotation<T>() where T : class public T? Annotation<T>() where T : class
{ {
object annotations = this.annotations; object? annotations = this.annotations;
AnnotationList list = annotations as AnnotationList; AnnotationList? list = annotations as AnnotationList;
if (list != null) if (list != null)
{ {
lock (list) lock (list)
{ {
foreach (object obj in list) foreach (object obj in list)
{ {
T t = obj as T; T? t = obj as T;
if (t != null) if (t != null)
return t; return t;
} }
@ -232,12 +234,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
} }
public object Annotation(Type type) public object? Annotation(Type type)
{ {
if (type == null) if (type == null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
object annotations = this.annotations; object? annotations = this.annotations;
AnnotationList list = annotations as AnnotationList; AnnotationList? list = annotations as AnnotationList;
if (list != null) if (list != null)
{ {
lock (list) lock (list)
@ -262,8 +264,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary> /// </summary>
public IEnumerable<object> Annotations { public IEnumerable<object> Annotations {
get { get {
object annotations = this.annotations; object? annotations = this.annotations;
AnnotationList list = annotations as AnnotationList; AnnotationList? list = annotations as AnnotationList;
if (list != null) if (list != null)
{ {
lock (list) lock (list)

2
ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs

@ -26,6 +26,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
#nullable enable
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;

10
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
{ {
/// <summary> /// <summary>
@ -24,18 +26,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
/// <remarks>Does not match null nodes.</remarks> /// <remarks>Does not match null nodes.</remarks>
public class AnyNode : Pattern public class AnyNode : Pattern
{ {
readonly string groupName; readonly string? groupName;
public string GroupName { public string? GroupName {
get { return groupName; } get { return groupName; }
} }
public AnyNode(string groupName = null) public AnyNode(string? groupName = null)
{ {
this.groupName = groupName; this.groupName = groupName;
} }
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode? other, Match match)
{ {
match.Add(this.groupName, other); match.Add(this.groupName, other);
return other != null; return other != null;

10
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
{ {
/// <summary> /// <summary>
@ -32,18 +34,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
/// <remarks>Does not match null nodes.</remarks> /// <remarks>Does not match null nodes.</remarks>
public class AnyNodeOrNull : Pattern public class AnyNodeOrNull : Pattern
{ {
readonly string groupName; readonly string? groupName;
public string GroupName { public string? GroupName {
get { return groupName; } get { return groupName; }
} }
public AnyNodeOrNull(string groupName = null) public AnyNodeOrNull(string? groupName = null)
{ {
this.groupName = groupName; this.groupName = groupName;
} }
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode? other, Match match)
{ {
if (other == null) if (other == null)
{ {

8
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Linq; using System.Linq;
@ -39,11 +41,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
this.referencedGroupName = referencedGroupName; 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(); var last = match.Get(referencedGroupName).LastOrDefault();
if (last == null && other == null) if (last == null)
return true; return other == null;
return last.IsMatch(other); return last.IsMatch(other);
} }
} }

2
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching

4
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -43,7 +45,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
alternatives.Add(alternative); alternatives.Add(alternative);
} }
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode? other, Match match)
{ {
var checkPoint = match.CheckPoint(); var checkPoint = match.CheckPoint();
foreach (INode alt in alternatives) foreach (INode alt in alternatives)

8
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -26,7 +28,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
/// </summary> /// </summary>
public interface INode public interface INode
{ {
bool DoMatch(INode other, Match match); bool DoMatch(INode? other, Match match);
/// <summary> /// <summary>
/// Matches this pattern node against the collection element at <paramref name="pos"/>. /// Matches this pattern node against the collection element at <paramref name="pos"/>.
@ -51,7 +53,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
/// However, it is also possible to match two ASTs without any pattern nodes - /// 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. /// doing so will produce a successful match if the two ASTs are structurally identical.
/// </remarks> /// </remarks>
public static Match Match(this INode pattern, INode other) public static Match Match(this INode pattern, INode? other)
{ {
if (pattern == null) if (pattern == null)
throw new ArgumentNullException(nameof(pattern)); throw new ArgumentNullException(nameof(pattern));
@ -62,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
return default(PatternMatching.Match); 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) if (pattern == null)
throw new ArgumentNullException(nameof(pattern)); throw new ArgumentNullException(nameof(pattern));

2
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
{ {
/// <summary> /// <summary>

4
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Linq; using System.Linq;
@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
this.referencedGroupName = referencedGroupName; this.referencedGroupName = referencedGroupName;
} }
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode? other, Match match)
{ {
var ident = other as IdentifierExpression; var ident = other as IdentifierExpression;
if (ident == null || ident.TypeArguments.Any()) if (ident == null || ident.TypeArguments.Any())

18
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching 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; }) // TODO: maybe we should add an implicit Match->bool conversion? (implicit operator bool(Match m) { return m != null; })
List<KeyValuePair<string, INode>> results; List<KeyValuePair<string, INode?>> results;
public bool Success { public bool Success {
get { return results != null; } get { return results != null; }
@ -36,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
internal static Match CreateNew() internal static Match CreateNew()
{ {
Match m; Match m;
m.results = new List<KeyValuePair<string, INode>>(); m.results = new List<KeyValuePair<string, INode?>>();
return m; return m;
} }
@ -50,7 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
results.RemoveRange(checkPoint, results.Count - checkPoint); results.RemoveRange(checkPoint, results.Count - checkPoint);
} }
public IEnumerable<INode> Get(string groupName) public IEnumerable<INode?> Get(string groupName)
{ {
if (results == null) if (results == null)
yield break; yield break;
@ -68,7 +70,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
foreach (var pair in results) foreach (var pair in results)
{ {
if (pair.Key == groupName) 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; return false;
} }
public void Add(string groupName, INode node) public void Add(string? groupName, INode? node)
{ {
if (groupName != null && node != null) if (groupName != null && node != null)
{ {
results.Add(new KeyValuePair<string, INode>(groupName, node)); results.Add(new KeyValuePair<string, INode?>(groupName, node));
} }
} }
internal void AddNull(string groupName) internal void AddNull(string? groupName)
{ {
if (groupName != null) if (groupName != null)
{ {
results.Add(new KeyValuePair<string, INode>(groupName, null)); results.Add(new KeyValuePair<string, INode?>(groupName, null));
} }
} }
} }

4
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
@ -44,7 +46,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
this.childNode = childNode; this.childNode = childNode;
} }
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode? other, Match match)
{ {
match.Add(this.groupName, other); match.Add(this.groupName, other);
return childNode.DoMatch(other, match); return childNode.DoMatch(other, match);

4
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -48,7 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
return childNode.DoMatch(pos < other.Count ? other[pos] : null, match); 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) if (other == null)
return true; return true;

6
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -31,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
/// </summary> /// </summary>
public static readonly string AnyString = "$any$"; 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; 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<INode> other, int pos, Match match, BacktrackingInfo backtrackingInfo) public virtual bool DoMatchCollection(IReadOnlyList<INode> other, int pos, Match match, BacktrackingInfo backtrackingInfo)
{ {

4
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Collections.Generic; 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. 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) if (other == null)
return this.MinCount <= 0; return this.MinCount <= 0;

2
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public static class Roles public static class Roles

2
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax

6
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
@ -35,11 +37,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return (Statement)base.Clone(); return (Statement)base.Clone();
} }
public Statement ReplaceWith(Func<Statement, Statement> replaceFunction) public Statement? ReplaceWith(Func<Statement, Statement> replaceFunction)
{ {
if (replaceFunction == null) if (replaceFunction == null)
throw new ArgumentNullException(nameof(replaceFunction)); throw new ArgumentNullException(nameof(replaceFunction));
return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); return (Statement?)base.ReplaceWith(node => replaceFunction((Statement)node));
} }
} }
} }

10
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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>
@ -51,15 +53,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
|| operatorType == BinaryOperatorType.ExclusiveOr; || 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)) while (next != null && !(next is Statement))
next = next.NextSibling; 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; var simpleType = type as SimpleType;
return simpleType != null && simpleType.Identifier == "__arglist"; return simpleType != null && simpleType.Identifier == "__arglist";

12
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
@ -98,7 +100,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <summary> /// <summary>
/// Equality test. /// Equality test.
/// </summary> /// </summary>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (!(obj is TextLocation)) if (!(obj is TextLocation))
return false; return false;
@ -187,17 +189,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class TextLocationConverter : TypeConverter 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); 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); 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) if (value is string)
{ {
@ -210,7 +212,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return base.ConvertFrom(context, culture, value); 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) if (value is TextLocation)
{ {

2
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;

10
ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
if (!context.Settings.QueryExpressions) if (!context.Settings.QueryExpressions)
return; return;
CombineQueries(rootNode, new Dictionary<string, object>()); CombineQueries(rootNode, new Dictionary<string, object?>());
} }
static readonly InvocationExpression castPattern = new InvocationExpression { static readonly InvocationExpression castPattern = new InvocationExpression {
@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
}; };
void CombineQueries(AstNode node, Dictionary<string, object> fromOrLetIdentifiers) void CombineQueries(AstNode node, Dictionary<string, object?> fromOrLetIdentifiers)
{ {
AstNode? next; AstNode? next;
for (AstNode? child = node.FirstChild; child != null; child = 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<string, object> letClauses) bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, Dictionary<string, object?> letClauses)
{ {
if (!CSharpDecompiler.IsTransparentIdentifier(fromClause.Identifier)) if (!CSharpDecompiler.IsTransparentIdentifier(fromClause.Identifier))
return false; return false;
@ -161,7 +161,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary> /// <summary>
/// Removes all occurrences of transparent identifiers /// Removes all occurrences of transparent identifiers
/// </summary> /// </summary>
void RemoveTransparentIdentifierReferences(AstNode node, Dictionary<string, object> fromOrLetIdentifiers) void RemoveTransparentIdentifierReferences(AstNode node, Dictionary<string, object?> fromOrLetIdentifiers)
{ {
foreach (AstNode child in node.Children) foreach (AstNode child in node.Children)
{ {
@ -174,7 +174,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
mre.TypeArguments.MoveTo(newIdent.TypeArguments); mre.TypeArguments.MoveTo(newIdent.TypeArguments);
newIdent.CopyAnnotationsFrom(mre); newIdent.CopyAnnotationsFrom(mre);
newIdent.RemoveAnnotations<Semantics.MemberResolveResult>(); // remove the reference to the property of the anonymous type newIdent.RemoveAnnotations<Semantics.MemberResolveResult>(); // 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); newIdent.AddAnnotation(annotation);
mre.ReplaceWith(newIdent); mre.ReplaceWith(newIdent);
return; return;

6
ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
this.name = type.Name; 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; ComposedType? ct = other as ComposedType;
AstType? o; AstType? o;
@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
this.childNode = new AnyNode(groupName); 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; InvocationExpression? ie = other as InvocationExpression;
if (ie != null && ie.Annotation<LdTokenAnnotation>() != null && ie.Arguments.Count == 1) if (ie != null && ie.Annotation<LdTokenAnnotation>() != null && ie.Arguments.Count == 1)
@ -107,7 +107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
), "TypeHandle"); ), "TypeHandle");
} }
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode? other, Match match)
{ {
return childNode.DoMatch(other, match); return childNode.DoMatch(other, match);
} }

9
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

@ -48,7 +48,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
this.context = context; this.context = context;
this.conversions = CSharpConversions.Get(context.TypeSystem); this.conversions = CSharpConversions.Get(context.TypeSystem);
InitializeContext(rootNode.Annotation<UsingScope>()); // The decompiler attaches a UsingScope annotation to the syntax-tree root.
InitializeContext(rootNode.Annotation<UsingScope>()!);
rootNode.AcceptVisitor(this); rootNode.AcceptVisitor(this);
} }
@ -111,14 +112,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out) if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out)
return; return;
firstArgument = dirExpr.Expression; // A ref/out direction expression always wraps an operand.
firstArgument = dirExpr.Expression!;
target = firstArgument.GetResolveResult(); target = firstArgument.GetResolveResult();
dirExpr.Detach(); dirExpr.Detach();
} }
else if (firstArgument is NullReferenceExpression) else if (firstArgument is NullReferenceExpression)
{ {
Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace)); 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) if (invocationExpression.Target is IdentifierExpression identifierExpression)
{ {

6
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -168,7 +168,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Projection = elementSelector.Detach(), Projection = elementSelector.Detach(),
Key = keySelector.Detach() Key = keySelector.Detach()
}; };
queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation<IL.ILFunction>(), projectionLambda.Annotation<IL.ILFunction>())); // The matched group-by lambdas always carry their ILFunction annotation.
queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation<IL.ILFunction>()!, projectionLambda.Annotation<IL.ILFunction>()!));
query.Clauses.Add(queryGroupClause); query.Clauses.Add(queryGroupClause);
return query; return query;
} }
@ -314,7 +315,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
joinClause.IntoIdentifier = p2.Name; // into p2.Name joinClause.IntoIdentifier = p2.Name; // into p2.Name
joinClause.IntoIdentifierToken!.CopyAnnotationsFrom(p2); joinClause.IntoIdentifierToken!.CopyAnnotationsFrom(p2);
} }
joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation<IL.ILFunction>(), innerLambda.Annotation<IL.ILFunction>())); // The matched join key-selector lambdas always carry their ILFunction annotation.
joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation<IL.ILFunction>()!, innerLambda.Annotation<IL.ILFunction>()!));
query.Clauses.Add(joinClause); query.Clauses.Add(joinClause);
query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }.CopyAnnotationsFrom(lambda)); query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }.CopyAnnotationsFrom(lambda));
return query; return query;

2
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs

@ -379,7 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override void VisitSimpleType(SimpleType simpleType) public override void VisitSimpleType(SimpleType simpleType)
{ {
TypeResolveResult rr; TypeResolveResult? rr;
if ((rr = simpleType.Annotation<TypeResolveResult>()) == null) if ((rr = simpleType.Annotation<TypeResolveResult>()) == null)
{ {
base.VisitSimpleType(simpleType); base.VisitSimpleType(simpleType);

16
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return declPoint.Ancestors.Contains(loop) && !declareVariables.WasMerged(itemVar); 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) 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)) if (!MatchForeachOnMultiDimArray(upperBounds, collection, stmt, out var foreachVariable, out var statements, out var lowerBounds))
return null; return null;
statementsToDelete.Add(stmt); 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(); var itemVariable = foreachVariable.GetILVariable();
if (itemVariable == null || !itemVariable.IsSingleDefinition if (itemVariable == null || !itemVariable.IsSingleDefinition
|| (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot) || (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot)
@ -1098,7 +1099,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
dd.CopyAnnotationsFrom(methodDef); dd.CopyAnnotationsFrom(methodDef);
dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override); dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override);
dd.Body = m.Get<BlockStatement>("body").Single().Detach(); dd.Body = m.Get<BlockStatement>("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); methodDef.ReplaceWith(dd);
return dd; return dd;
} }
@ -1188,9 +1191,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var bAndC = expr.Right as BinaryOperatorExpression; var bAndC = expr.Right as BinaryOperatorExpression;
if (bAndC != null && bAndC.Operator == expr.Operator) if (bAndC != null && bAndC.Operator == expr.Operator)
{ {
// make bAndC the parent and expr the child // make bAndC the parent and expr the child.
var b = bAndC.Left.Detach(); // A conditional-and/or operator always has both operands present.
var c = bAndC.Right.Detach(); var b = bAndC.Left!.Detach();
var c = bAndC.Right!.Detach();
expr.ReplaceWith(bAndC.Detach()); expr.ReplaceWith(bAndC.Detach());
bAndC.Left = expr; bAndC.Left = expr;
bAndC.Right = c; bAndC.Right = c;

Loading…
Cancel
Save