Browse Source

Replace AST null-objects with nullable reference types

With every optional slot nullable, the null-object pattern is dead. Generated
non-nullable getters return the backing field directly, which surfaced a last
tier of slots the decompiler legitimately leaves empty (omitted range operands,
an implicitly-typed array creation, unnamed parameters, an unbound generic
argument, and others) and flips them to nullable too. The machinery is then
removed entirely: the per-node null classes, the .Null statics and
VisitNullNode, AstNode.IsNull, the role null object, and Identifier.Null.
AcceptVisitor becomes unconditionally generated, and consumers move from
.IsNull to is null and from unconditional visits to ?.AcceptVisitor.
Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3807/head
Siegfried Pammer 3 weeks ago committed by Siegfried Pammer
parent
commit
9aa0ddd37b
  1. 59
      ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs
  2. 6
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 6
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  4. 4
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  5. 48
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  6. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs
  7. 2
      ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs
  8. 36
      ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs
  9. 4
      ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs
  10. 26
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  11. 4
      ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs
  12. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs
  13. 12
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs
  14. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs
  15. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs
  16. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs
  17. 20
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/QueryExpression.cs
  18. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs
  19. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs
  20. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs
  21. 2
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs
  22. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs
  23. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs
  24. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs
  25. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs
  26. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs
  27. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs
  28. 2
      ICSharpCode.Decompiler/CSharp/Syntax/MemberType.cs
  29. 2
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs
  30. 2
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs
  31. 2
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs
  32. 4
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs
  33. 2
      ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs
  34. 26
      ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs
  35. 2
      ICSharpCode.Decompiler/CSharp/Syntax/SimpleType.cs
  36. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs
  37. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs
  38. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs
  39. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs
  40. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs
  41. 2
      ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs
  42. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs
  43. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs
  44. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs
  45. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs
  46. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs
  47. 6
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs
  48. 15
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs
  49. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs
  50. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs
  51. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs
  52. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
  53. 8
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs
  54. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  55. 2
      ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs
  56. 2
      ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs
  57. 1
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs
  58. 4
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  59. 4
      ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
  60. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs
  61. 2
      ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs
  62. 2
      ICSharpCode.Decompiler/IL/ILAmbience.cs
  63. 2
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  64. 2
      ILSpy/Languages/CSharpLanguage.cs

59
ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs

@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.Generators;
[Generator] [Generator]
internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
{ {
record AstNodeAdditions(string NodeName, bool NeedsAcceptImpls, bool NeedsVisitor, bool NeedsNullNode, bool NeedsPatternPlaceholder, int NullNodeBaseCtorParamCount, bool IsTypeNode, string VisitMethodName, string VisitMethodParamType, EquatableArray<(string Member, string TypeName, bool RecursiveMatch, bool MatchAny, bool Nullable)>? MembersToMatch, EquatableArray<(string RoleExpr, bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable, string KindName, bool IsPartial)>? Slots, EquatableArray<(string StringName, string TokenName, bool NullOnEmpty)>? NameSlots); record AstNodeAdditions(string NodeName, bool NeedsVisitor, bool NeedsNullNode, bool NeedsPatternPlaceholder, int NullNodeBaseCtorParamCount, bool IsTypeNode, string VisitMethodName, string VisitMethodParamType, EquatableArray<(string Member, string TypeName, bool RecursiveMatch, bool MatchAny, bool Nullable)>? MembersToMatch, EquatableArray<(string RoleExpr, bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable, string KindName, bool IsPartial)>? Slots, EquatableArray<(string StringName, string TokenName, bool NullOnEmpty)>? NameSlots);
// Derives the shared SlotKind name from a [Slot] role expression: the last dotted segment with a // Derives the shared SlotKind name from a [Slot] role expression: the last dotted segment with a
// trailing "Role" removed (e.g. "Roles.EmbeddedStatement" -> "EmbeddedStatement", "LeftRole" -> // trailing "Role" removed (e.g. "Roles.EmbeddedStatement" -> "EmbeddedStatement", "LeftRole" ->
@ -80,7 +80,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
if (!excludeName) if (!excludeName)
membersToMatch.Add(("Name", "String", false, false, false)); membersToMatch.Add(("Name", "String", false, false, false));
membersToMatch.Add(("MatchAttributesAndModifiers", null!, false, false, false)); membersToMatch.Add(("MatchAttributesAndModifiers", null!, false, false, false));
membersToMatch.Add(("ReturnType", "AstType", true, false, false)); membersToMatch.Add(("ReturnType", "AstType", true, false, true));
} }
foreach (var m in targetSymbol.GetMembers()) foreach (var m in targetSymbol.GetMembers())
@ -149,7 +149,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
} }
} }
return new(targetSymbol.Name, !targetSymbol.MemberNames.Contains("AcceptVisitor"), return new(targetSymbol.Name,
NeedsVisitor: !targetSymbol.IsAbstract && targetSymbol.BaseType!.IsAbstract, NeedsVisitor: !targetSymbol.IsAbstract && targetSymbol.BaseType!.IsAbstract,
NeedsNullNode: (bool)attribute.ConstructorArguments[0].Value!, NeedsNullNode: (bool)attribute.ConstructorArguments[0].Value!,
NeedsPatternPlaceholder: (bool)attribute.ConstructorArguments[1].Value!, NeedsPatternPlaceholder: (bool)attribute.ConstructorArguments[1].Value!,
@ -184,51 +184,6 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
builder.AppendLine($"partial class {source.NodeName}"); builder.AppendLine($"partial class {source.NodeName}");
builder.AppendLine("{"); builder.AppendLine("{");
if (source.NeedsNullNode)
{
bool needsNew = source.NodeName != "AstNode";
builder.AppendLine($" {(needsNew ? "new " : "")}public static readonly {source.NodeName} Null = new Null{source.NodeName}();");
builder.AppendLine($@"
sealed class Null{source.NodeName} : {source.NodeName}
{{
public override bool IsNull => true;
public override void AcceptVisitor(IAstVisitor visitor)
{{
visitor.VisitNullNode(this);
}}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{{
return visitor.VisitNullNode(this);
}}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{{
return visitor.VisitNullNode(this, data);
}}
protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match)
{{
return other == null || other.IsNull;
}}");
if (source.NullNodeBaseCtorParamCount > 0)
{
builder.AppendLine($@"
public Null{source.NodeName}() : base({string.Join(", ", Enumerable.Repeat("default", source.NullNodeBaseCtorParamCount))}) {{ }}");
}
builder.AppendLine($@"
}}
");
}
if (source.NeedsPatternPlaceholder) if (source.NeedsPatternPlaceholder)
{ {
// The placeholder conversion is part of the pattern-construction DSL, where a non-null // The placeholder conversion is part of the pattern-construction DSL, where a non-null
@ -283,7 +238,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
); );
} }
if (source.NeedsAcceptImpls && source.NeedsVisitor) if (source.NeedsVisitor)
{ {
builder.Append($@" public override void AcceptVisitor(IAstVisitor visitor) builder.Append($@" public override void AcceptVisitor(IAstVisitor visitor)
{{ {{
@ -307,7 +262,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
{ {
builder.Append($@" protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) builder.Append($@" protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match)
{{ {{
return other is {source.NodeName} o && !o.IsNull"); return other is {source.NodeName} o");
foreach (var (member, typeName, recursive, hasAny, nullable) in source.MembersToMatch) foreach (var (member, typeName, recursive, hasAny, nullable) in source.MembersToMatch)
{ {
@ -366,7 +321,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
builder.AppendLine($"\t{type}? {field};"); builder.AppendLine($"\t{type}? {field};");
builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}{type}{(isNullable ? "?" : "")} {name}"); builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}{type}{(isNullable ? "?" : "")} {name}");
builder.AppendLine("\t{"); builder.AppendLine("\t{");
builder.AppendLine($"\t\tget => {field}{(isNullable ? "" : $" ?? {roleExpr}.NullObject")};"); builder.AppendLine($"\t\tget => {field}{(isNullable ? "" : "!")};");
builder.AppendLine($"\t\tset => SetChildNode(ref {field}, value, {roleExpr});"); builder.AppendLine($"\t\tset => SetChildNode(ref {field}, value, {roleExpr});");
builder.AppendLine("\t}"); builder.AppendLine("\t}");
} }
@ -514,7 +469,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;"); builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;");
source = source source = source
.Concat([new("NullNode", false, true, false, false, 0, false, "NullNode", "AstNode", null, null, null), new("PatternPlaceholder", false, true, false, false, 0, false, "PatternPlaceholder", "AstNode", null, null, null)]) .Concat([new("PatternPlaceholder", true, false, false, 0, false, "PatternPlaceholder", "AstNode", null, null, null)])
.ToImmutableArray(); .ToImmutableArray();
WriteInterface("IAstVisitor", "void", ""); WriteInterface("IAstVisitor", "void", "");

6
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1406,7 +1406,7 @@ namespace ICSharpCode.Decompiler.CSharp
EntityDeclaration memberDecl, IMethod method, EntityDeclaration memberDecl, IMethod method,
TypeSystemAstBuilder astBuilder) TypeSystemAstBuilder astBuilder)
{ {
if (!memberDecl.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole).IsNull) if (memberDecl.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole) is not null)
{ {
yield break; // cannot create forwarder for existing explicit interface impl yield break; // cannot create forwarder for existing explicit interface impl
} }
@ -1427,7 +1427,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (m == null || m.DeclaringType.Kind != TypeKind.Interface) if (m == null || m.DeclaringType.Kind != TypeKind.Interface)
continue; continue;
var methodDecl = new MethodDeclaration(); var methodDecl = new MethodDeclaration();
methodDecl.ReturnType = memberDecl.ReturnType.Clone(); methodDecl.ReturnType = memberDecl.ReturnType?.Clone();
methodDecl.PrivateImplementationType = astBuilder.ConvertType(m.DeclaringType); methodDecl.PrivateImplementationType = astBuilder.ConvertType(m.DeclaringType);
methodDecl.Name = m.Name; methodDecl.Name = m.Name;
methodDecl.TypeParameters.AddRange(memberDecl.GetChildrenByRole(Roles.TypeParameter) methodDecl.TypeParameters.AddRange(memberDecl.GetChildrenByRole(Roles.TypeParameter)
@ -2041,7 +2041,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
var body = BlockStatement.Null; BlockStatement body = new BlockStatement();
MethodBodyBlock methodBody; MethodBodyBlock methodBody;
try try
{ {

6
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -2182,19 +2182,19 @@ namespace ICSharpCode.Decompiler.CSharp
} }
else if (callOpCode == OpCode.Call && method.Name == "get_All" && argumentList.Length == 0) else if (callOpCode == OpCode.Call && method.Name == "get_All" && argumentList.Length == 0)
{ {
result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, Expression.Null) result = new BinaryOperatorExpression(null, BinaryOperatorType.Range, null)
.WithRR(new MemberResolveResult(null, method.AccessorOwner ?? method)); .WithRR(new MemberResolveResult(null, method.AccessorOwner ?? method));
return true; return true;
} }
else if (callOpCode == OpCode.Call && method.Name == "StartAt" && argumentList.Length == 1) else if (callOpCode == OpCode.Call && method.Name == "StartAt" && argumentList.Length == 1)
{ {
result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, Expression.Null) result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, null)
.WithRR(new MemberResolveResult(null, method)); .WithRR(new MemberResolveResult(null, method));
return true; return true;
} }
else if (callOpCode == OpCode.Call && method.Name == "EndAt" && argumentList.Length == 1) else if (callOpCode == OpCode.Call && method.Name == "EndAt" && argumentList.Length == 1)
{ {
result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, argumentList.Arguments[0]) result = new BinaryOperatorExpression(null, BinaryOperatorType.Range, argumentList.Arguments[0])
.WithRR(new MemberResolveResult(null, method)); .WithRR(new MemberResolveResult(null, method));
return true; return true;
} }

4
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
&& (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType)
{ {
var rt = node.GetChildByRole(Roles.Type); var rt = node.GetChildByRole(Roles.Type);
if (!rt.IsNull) if (rt is not null)
{ {
rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy));
writer.Space(); writer.Space();
@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
&& (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType)
{ {
var rt = node.GetChildByRole(Roles.Type); var rt = node.GetChildByRole(Roles.Type);
if (!rt.IsNull) if (rt is not null)
{ {
writer.Space(); writer.Space();
writer.WriteToken(Roles.Colon, ":"); writer.WriteToken(Roles.Colon, ":");

48
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -502,7 +502,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
/// </remarks> /// </remarks>
protected virtual void WriteEmbeddedStatement(Statement embeddedStatement, NewLinePlacement nlp = NewLinePlacement.NewLine) protected virtual void WriteEmbeddedStatement(Statement embeddedStatement, NewLinePlacement nlp = NewLinePlacement.NewLine)
{ {
if (embeddedStatement.IsNull) if (embeddedStatement is null)
{ {
NewLine(); NewLine();
return; return;
@ -531,7 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style, bool newLine = true) protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style, bool newLine = true)
{ {
if (body is null || body.IsNull) if (body is null)
{ {
Semicolon(); Semicolon();
} }
@ -552,7 +552,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
protected virtual void WritePrivateImplementationType(AstType privateImplementationType) protected virtual void WritePrivateImplementationType(AstType privateImplementationType)
{ {
if (privateImplementationType is not null && !privateImplementationType.IsNull) if (privateImplementationType is not null)
{ {
privateImplementationType.AcceptVisitor(this); privateImplementationType.AcceptVisitor(this);
WriteToken(Roles.Dot); WriteToken(Roles.Dot);
@ -611,7 +611,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
StartNode(arrayCreateExpression); StartNode(arrayCreateExpression);
WriteKeyword(ArrayCreateExpression.NewKeywordRole); WriteKeyword(ArrayCreateExpression.NewKeywordRole);
arrayCreateExpression.Type.AcceptVisitor(this); arrayCreateExpression.Type?.AcceptVisitor(this);
if (arrayCreateExpression.Arguments.Count > 0) if (arrayCreateExpression.Arguments.Count > 0)
{ {
WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments);
@ -765,7 +765,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
{ {
StartNode(binaryOperatorExpression); StartNode(binaryOperatorExpression);
binaryOperatorExpression.Left.AcceptVisitor(this); binaryOperatorExpression.Left?.AcceptVisitor(this);
bool spacePolicy; bool spacePolicy;
switch (binaryOperatorExpression.Operator) switch (binaryOperatorExpression.Operator)
{ {
@ -823,7 +823,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteToken(tokenRole); WriteToken(tokenRole);
} }
Space(spacePolicy); Space(spacePolicy);
binaryOperatorExpression.Right.AcceptVisitor(this); binaryOperatorExpression.Right?.AcceptVisitor(this);
EndNode(binaryOperatorExpression); EndNode(binaryOperatorExpression);
} }
@ -1361,7 +1361,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(queryFromClause); StartNode(queryFromClause);
WriteKeyword(QueryFromClause.FromKeywordRole); WriteKeyword(QueryFromClause.FromKeywordRole);
Space(); Space();
queryFromClause.Type.AcceptVisitor(this); queryFromClause.Type?.AcceptVisitor(this);
Space(); Space();
WriteIdentifier(queryFromClause.IdentifierToken); WriteIdentifier(queryFromClause.IdentifierToken);
Space(); Space();
@ -1397,7 +1397,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
StartNode(queryJoinClause); StartNode(queryJoinClause);
WriteKeyword(QueryJoinClause.JoinKeywordRole); WriteKeyword(QueryJoinClause.JoinKeywordRole);
queryJoinClause.Type.AcceptVisitor(this); queryJoinClause.Type?.AcceptVisitor(this);
Space(); Space();
WriteIdentifier(queryJoinClause.JoinIdentifierToken); WriteIdentifier(queryJoinClause.JoinIdentifierToken);
Space(); Space();
@ -1523,7 +1523,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteAttributes(delegateDeclaration.Attributes); WriteAttributes(delegateDeclaration.Attributes);
WriteModifiers(delegateDeclaration.Modifiers); WriteModifiers(delegateDeclaration.Modifiers);
WriteKeyword(Roles.DelegateKeyword); WriteKeyword(Roles.DelegateKeyword);
delegateDeclaration.ReturnType.AcceptVisitor(this); delegateDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WriteIdentifier(delegateDeclaration.NameToken); WriteIdentifier(delegateDeclaration.NameToken);
WriteTypeParameters(delegateDeclaration.TypeParameters); WriteTypeParameters(delegateDeclaration.TypeParameters);
@ -2416,7 +2416,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteAttributes(eventDeclaration.Attributes); WriteAttributes(eventDeclaration.Attributes);
WriteModifiers(eventDeclaration.Modifiers); WriteModifiers(eventDeclaration.Modifiers);
WriteKeyword(EventDeclaration.EventKeywordRole); WriteKeyword(EventDeclaration.EventKeywordRole);
eventDeclaration.ReturnType.AcceptVisitor(this); eventDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WriteCommaSeparatedList(eventDeclaration.Variables); WriteCommaSeparatedList(eventDeclaration.Variables);
Semicolon(); Semicolon();
@ -2429,7 +2429,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteAttributes(customEventDeclaration.Attributes); WriteAttributes(customEventDeclaration.Attributes);
WriteModifiers(customEventDeclaration.Modifiers); WriteModifiers(customEventDeclaration.Modifiers);
WriteKeyword(CustomEventDeclaration.EventKeywordRole); WriteKeyword(CustomEventDeclaration.EventKeywordRole);
customEventDeclaration.ReturnType.AcceptVisitor(this); customEventDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType);
WriteIdentifier(customEventDeclaration.NameToken); WriteIdentifier(customEventDeclaration.NameToken);
@ -2452,7 +2452,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(fieldDeclaration); StartNode(fieldDeclaration);
WriteAttributes(fieldDeclaration.Attributes); WriteAttributes(fieldDeclaration.Attributes);
WriteModifiers(fieldDeclaration.Modifiers); WriteModifiers(fieldDeclaration.Modifiers);
fieldDeclaration.ReturnType.AcceptVisitor(this); fieldDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WriteCommaSeparatedList(fieldDeclaration.Variables); WriteCommaSeparatedList(fieldDeclaration.Variables);
Semicolon(); Semicolon();
@ -2466,7 +2466,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteModifiers(fixedFieldDeclaration.Modifiers); WriteModifiers(fixedFieldDeclaration.Modifiers);
WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); WriteKeyword(FixedFieldDeclaration.FixedKeywordRole);
Space(); Space();
fixedFieldDeclaration.ReturnType.AcceptVisitor(this); fixedFieldDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WriteCommaSeparatedList(fixedFieldDeclaration.Variables); WriteCommaSeparatedList(fixedFieldDeclaration.Variables);
Semicolon(); Semicolon();
@ -2477,7 +2477,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
StartNode(fixedVariableInitializer); StartNode(fixedVariableInitializer);
WriteIdentifier(fixedVariableInitializer.NameToken); WriteIdentifier(fixedVariableInitializer.NameToken);
if (!fixedVariableInitializer.CountExpression.IsNull) if (fixedVariableInitializer.CountExpression is not null)
{ {
WriteToken(Roles.LBracket); WriteToken(Roles.LBracket);
Space(policy.SpacesWithinBrackets); Space(policy.SpacesWithinBrackets);
@ -2493,7 +2493,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(indexerDeclaration); StartNode(indexerDeclaration);
WriteAttributes(indexerDeclaration.Attributes); WriteAttributes(indexerDeclaration.Attributes);
WriteModifiers(indexerDeclaration.Modifiers); WriteModifiers(indexerDeclaration.Modifiers);
indexerDeclaration.ReturnType.AcceptVisitor(this); indexerDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType);
WriteKeyword(IndexerDeclaration.ThisKeywordRole); WriteKeyword(IndexerDeclaration.ThisKeywordRole);
@ -2538,7 +2538,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(methodDeclaration); StartNode(methodDeclaration);
WriteAttributes(methodDeclaration.Attributes); WriteAttributes(methodDeclaration.Attributes);
WriteModifiers(methodDeclaration.Modifiers); WriteModifiers(methodDeclaration.Modifiers);
methodDeclaration.ReturnType.AcceptVisitor(this); methodDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); WritePrivateImplementationType(methodDeclaration.PrivateImplementationType);
WriteIdentifier(methodDeclaration.NameToken); WriteIdentifier(methodDeclaration.NameToken);
@ -2568,7 +2568,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
else else
{ {
operatorDeclaration.ReturnType.AcceptVisitor(this); operatorDeclaration.ReturnType?.AcceptVisitor(this);
} }
Space(); Space();
WritePrivateImplementationType(operatorDeclaration.PrivateImplementationType); WritePrivateImplementationType(operatorDeclaration.PrivateImplementationType);
@ -2583,7 +2583,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
|| operatorDeclaration.OperatorType == OperatorType.CheckedExplicit || operatorDeclaration.OperatorType == OperatorType.CheckedExplicit
|| operatorDeclaration.OperatorType == OperatorType.Implicit) || operatorDeclaration.OperatorType == OperatorType.Implicit)
{ {
operatorDeclaration.ReturnType.AcceptVisitor(this); operatorDeclaration.ReturnType?.AcceptVisitor(this);
} }
else else
{ {
@ -2658,7 +2658,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(propertyDeclaration); StartNode(propertyDeclaration);
WriteAttributes(propertyDeclaration.Attributes); WriteAttributes(propertyDeclaration.Attributes);
WriteModifiers(propertyDeclaration.Modifiers); WriteModifiers(propertyDeclaration.Modifiers);
propertyDeclaration.ReturnType.AcceptVisitor(this); propertyDeclaration.ReturnType?.AcceptVisitor(this);
Space(); Space();
WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType);
WriteIdentifier(propertyDeclaration.NameToken); WriteIdentifier(propertyDeclaration.NameToken);
@ -2682,7 +2682,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
} }
CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine); CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine);
if (!propertyDeclaration.Initializer.IsNull) if (propertyDeclaration.Initializer is not null)
{ {
Space(policy.SpaceAroundAssignment); Space(policy.SpaceAroundAssignment);
WriteToken(Roles.Assign); WriteToken(Roles.Assign);
@ -2752,7 +2752,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitSimpleType(SimpleType simpleType) public virtual void VisitSimpleType(SimpleType simpleType)
{ {
StartNode(simpleType); StartNode(simpleType);
WriteIdentifier(simpleType.IdentifierToken); // An unbound generic type argument (the '' in 'typeof(List<>)') is a nameless SimpleType.
if (simpleType.IdentifierToken is not null)
WriteIdentifier(simpleType.IdentifierToken);
WriteTypeArguments(simpleType.TypeArguments); WriteTypeArguments(simpleType.TypeArguments);
EndNode(simpleType); EndNode(simpleType);
} }
@ -2956,10 +2958,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteIdentifier(identifier); WriteIdentifier(identifier);
} }
void IAstVisitor.VisitNullNode(AstNode nullNode)
{
}
void IAstVisitor.VisitErrorNode(AstNode errorNode) void IAstVisitor.VisitErrorNode(AstNode errorNode)
{ {
StartNode(errorNode); StartNode(errorNode);

2
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs

@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public override void WriteIdentifier(Identifier identifier) public override void WriteIdentifier(Identifier identifier)
{ {
AssignPendingStartLocations(); AssignPendingStartLocations();
if (!identifier.IsNull) if (identifier is not null)
identifier.SetStartLocation(locationProvider.Location); identifier.SetStartLocation(locationProvider.Location);
currentList.Add(identifier); currentList.Add(identifier);
base.WriteIdentifier(identifier); base.WriteIdentifier(identifier);

2
ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs

@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.CSharp
void VisitAsSequencePoint(AstNode node) void VisitAsSequencePoint(AstNode node)
{ {
if (node is null || node.IsNull) if (node is null)
return; return;
StartSequencePoint(node); StartSequencePoint(node);
node.AcceptVisitor(this); node.AcceptVisitor(this);

36
ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs

@ -57,12 +57,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1;
protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits; protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits;
public virtual bool IsNull {
get {
return false;
}
}
TextLocation startLocation = TextLocation.Empty; TextLocation startLocation = TextLocation.Empty;
TextLocation endLocation = TextLocation.Empty; TextLocation endLocation = TextLocation.Empty;
@ -480,7 +474,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
// A null or null-object value empties the slot. Called by generated single-slot property setters. // A null or null-object value empties the slot. Called by generated single-slot property setters.
internal void SetChildNode<T>(ref T? field, T? value, Role role) where T : AstNode internal void SetChildNode<T>(ref T? field, T? value, Role role) where T : AstNode
{ {
T? newValue = (value == null || value.IsNull) ? null : value; T? newValue = value;
if (field == newValue) if (field == newValue)
return; return;
if (newValue != null) if (newValue != null)
@ -520,14 +514,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
if (role == null) if (role == null)
throw new ArgumentNullException(nameof(role)); throw new ArgumentNullException(nameof(role));
if (child == null || child.IsNull) if (child == null)
return; return;
AddChildUnsafe(child, role); AddChildUnsafe(child, role);
} }
public void AddChildWithExistingRole(AstNode? child) public void AddChildWithExistingRole(AstNode? child)
{ {
if (child == null || child.IsNull) if (child == null)
return; return;
AddChildUnsafe(child, child.Role); AddChildUnsafe(child, child.Role);
} }
@ -555,7 +549,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
if (GetChildSlot(i) == role) if (GetChildSlot(i) == role)
{ {
SetChild(i, child == null || child.IsNull ? null : child); SetChild(i, child);
return; return;
} }
} }
@ -566,11 +560,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
if (role == null) if (role == null)
throw new ArgumentNullException(nameof(role)); throw new ArgumentNullException(nameof(role));
if (child == null || child.IsNull) if (child == null)
return; return;
AstNodeCollection? collection = GetCollectionByRole(role); AstNodeCollection? collection = GetCollectionByRole(role);
if (collection != null) if (collection != null)
collection.InsertNodeBefore((nextSibling == null || nextSibling.IsNull) ? null : nextSibling, child); collection.InsertNodeBefore(nextSibling, child);
else else
SetChildByRoleUntyped(role, child); SetChildByRoleUntyped(role, child);
} }
@ -588,11 +582,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
if (role == null) if (role == null)
throw new ArgumentNullException(nameof(role)); throw new ArgumentNullException(nameof(role));
if (child == null || child.IsNull) if (child == null)
return; return;
AstNodeCollection? collection = GetCollectionByRole(role); AstNodeCollection? collection = GetCollectionByRole(role);
if (collection != null) if (collection != null)
collection.InsertNodeAfter((prevSibling == null || prevSibling.IsNull) ? null : prevSibling, child); collection.InsertNodeAfter(prevSibling, child);
else else
SetChildByRoleUntyped(role, child); SetChildByRoleUntyped(role, child);
} }
@ -618,7 +612,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary> /// </summary>
public void ReplaceWith(AstNode? newNode) public void ReplaceWith(AstNode? newNode)
{ {
if (newNode == null || newNode.IsNull) if (newNode == null)
{ {
Remove(); Remove();
return; return;
@ -627,7 +621,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return; // nothing to do... return; // nothing to do...
if (parent == null) if (parent == null)
{ {
throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); throw new InvalidOperationException("Cannot replace the root node");
} }
parent.EnsureChildIndices(); parent.EnsureChildIndices();
Role role = parent.GetChildSlot(childIndex); Role role = parent.GetChildSlot(childIndex);
@ -660,7 +654,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
throw new ArgumentNullException(nameof(replaceFunction)); throw new ArgumentNullException(nameof(replaceFunction));
if (parent == null) if (parent == null)
{ {
throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); throw new InvalidOperationException("Cannot replace the root node");
} }
AstNode oldParent = parent; AstNode oldParent = parent;
AstNode? oldSuccessor = NextSibling; AstNode? oldSuccessor = NextSibling;
@ -669,7 +663,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
AstNode? replacement = replaceFunction(this); AstNode? replacement = replaceFunction(this);
if (oldSuccessor != null && oldSuccessor.parent != oldParent) if (oldSuccessor != null && oldSuccessor.parent != oldParent)
throw new InvalidOperationException("replace function changed nextSibling of node being replaced?"); throw new InvalidOperationException("replace function changed nextSibling of node being replaced?");
if (!(replacement == null || replacement.IsNull)) if (replacement != null)
{ {
if (replacement.parent != null) if (replacement.parent != null)
throw new InvalidOperationException("replace function must return the root of a tree"); throw new InvalidOperationException("replace function must return the root of a tree");
@ -839,8 +833,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </param> /// </param>
public virtual string ToString(CSharpFormattingOptions? formattingOptions) public virtual string ToString(CSharpFormattingOptions? formattingOptions)
{ {
if (IsNull)
return "";
var w = new StringWriter(); var w = new StringWriter();
AcceptVisitor(new CSharpOutputVisitor(w, formattingOptions ?? FormattingOptionsFactory.CreateMono())); AcceptVisitor(new CSharpOutputVisitor(w, formattingOptions ?? FormattingOptionsFactory.CreateMono()));
return w.ToString(); return w.ToString();
@ -897,15 +889,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public override void AddAnnotation(object annotation) public override void AddAnnotation(object annotation)
{ {
if (this.IsNull)
throw new InvalidOperationException("Cannot add annotations to the null node");
base.AddAnnotation(annotation); base.AddAnnotation(annotation);
} }
internal string DebugToString() internal string DebugToString()
{ {
if (IsNull)
return "Null";
string text = ToString(); string text = ToString();
text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " "); text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " ");
if (text.Length > 100) if (text.Length > 100)

4
ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs

@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public void Add(T element) public void Add(T element)
{ {
if (element == null || element.IsNull) if (element == null)
return; return;
ValidateNewChild(element); ValidateNewChild(element);
list.Add(element); list.Add(element);
@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void Insert(int index, T newItem) void Insert(int index, T newItem)
{ {
if (newItem == null || newItem.IsNull) if (newItem == null)
return; return;
ValidateNewChild(newItem); ValidateNewChild(newItem);
list.Insert(index, newItem); list.Insert(index, newItem);

26
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -43,14 +43,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
} }
public virtual void VisitNullNode(AstNode nullNode)
{
// Should we call VisitChildren here?
// We usually want to ignore null nodes.
// Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes;
// so changing this might break VisitChildren() overrides that expect the node to be part of the AST.
}
public virtual void VisitSyntaxTree(SyntaxTree syntaxTree) public virtual void VisitSyntaxTree(SyntaxTree syntaxTree)
{ {
VisitChildren(syntaxTree); VisitChildren(syntaxTree);
@ -726,15 +718,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return default(T); return default(T);
} }
public virtual T VisitNullNode(AstNode nullNode)
{
// Should we call VisitChildren here?
// We usually want to ignore null nodes.
// Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes;
// so changing this might break VisitChildren() overrides that expect the node to be part of the AST.
return default(T);
}
public virtual T VisitSyntaxTree(SyntaxTree unit) public virtual T VisitSyntaxTree(SyntaxTree unit)
{ {
return VisitChildren(unit); return VisitChildren(unit);
@ -1410,15 +1393,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return default(S); return default(S);
} }
public virtual S VisitNullNode(AstNode nullNode, T data)
{
// Should we call VisitChildren here?
// We usually want to ignore null nodes.
// Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes;
// so changing this might break VisitChildren() overrides that expect the node to be part of the AST.
return default(S);
}
public virtual S VisitSyntaxTree(SyntaxTree unit, T data) public virtual S VisitSyntaxTree(SyntaxTree unit, T data)
{ {
return VisitChildren(unit, data); return VisitChildren(unit, data);

4
ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs

@ -34,8 +34,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class DocumentationReference : AstNode public partial class DocumentationReference : AstNode
{ {
public static readonly Role<AstType> DeclaringTypeRole = new Role<AstType>("DeclaringType", AstType.Null); public static readonly Role<AstType> DeclaringTypeRole = new Role<AstType>("DeclaringType", null);
public static readonly Role<AstType> ConversionOperatorReturnTypeRole = new Role<AstType>("ConversionOperatorReturnType", AstType.Null); public static readonly Role<AstType> ConversionOperatorReturnTypeRole = new Role<AstType>("ConversionOperatorReturnType", null);
SymbolKind symbolKind; SymbolKind symbolKind;
OperatorType operatorType; OperatorType operatorType;

4
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs

@ -28,10 +28,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public readonly static TokenRole NewKeywordRole = new TokenRole("new"); public readonly static TokenRole NewKeywordRole = new TokenRole("new");
public readonly static Role<ArraySpecifier> AdditionalArraySpecifierRole = new Role<ArraySpecifier>("AdditionalArraySpecifier", null); public readonly static Role<ArraySpecifier> AdditionalArraySpecifierRole = new Role<ArraySpecifier>("AdditionalArraySpecifier", null);
public readonly static Role<ArrayInitializerExpression> InitializerRole = new Role<ArrayInitializerExpression>("Initializer", ArrayInitializerExpression.Null); public readonly static Role<ArrayInitializerExpression> InitializerRole = new Role<ArrayInitializerExpression>("Initializer", null);
[Slot("Roles.Type")] [Slot("Roles.Type")]
public partial AstType Type { get; set; } public partial AstType? Type { get; set; }
[Slot("Roles.Argument")] [Slot("Roles.Argument")]
public partial AstNodeCollection<Expression> Arguments { get; } public partial AstNodeCollection<Expression> Arguments { get; }

12
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs

@ -27,6 +27,8 @@
using System; using System;
using System.Linq.Expressions; using System.Linq.Expressions;
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>
@ -60,14 +62,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole RangeRole = new TokenRole(".."); public readonly static TokenRole RangeRole = new TokenRole("..");
public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole; public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole;
public readonly static Role<Expression> LeftRole = new Role<Expression>("Left", Expression.Null); public readonly static Role<Expression> LeftRole = new Role<Expression>("Left", null);
public readonly static Role<Expression> RightRole = new Role<Expression>("Right", Expression.Null); public readonly static Role<Expression> RightRole = new Role<Expression>("Right", null);
public BinaryOperatorExpression() public BinaryOperatorExpression()
{ {
} }
public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right) public BinaryOperatorExpression(Expression? left, BinaryOperatorType op, Expression? right)
{ {
this.Left = left; this.Left = left;
this.Operator = op; this.Operator = op;
@ -80,10 +82,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
[Slot(nameof(LeftRole))] [Slot(nameof(LeftRole))]
public partial Expression Left { get; set; } public partial Expression? Left { get; set; }
[Slot(nameof(RightRole))] [Slot(nameof(RightRole))]
public partial Expression Right { get; set; } public partial Expression? Right { get; set; }
public static TokenRole GetOperatorRole(BinaryOperatorType op) public static TokenRole GetOperatorRole(BinaryOperatorType op)
{ {

4
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs

@ -34,9 +34,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public readonly static Role<Expression> ConditionRole = Roles.Condition; public readonly static Role<Expression> ConditionRole = Roles.Condition;
public readonly static TokenRole QuestionMarkRole = new TokenRole("?"); public readonly static TokenRole QuestionMarkRole = new TokenRole("?");
public readonly static Role<Expression> TrueRole = new Role<Expression>("True", Expression.Null); public readonly static Role<Expression> TrueRole = new Role<Expression>("True", null);
public readonly static TokenRole ColonRole = Roles.Colon; public readonly static TokenRole ColonRole = Roles.Colon;
public readonly static Role<Expression> FalseRole = new Role<Expression>("False", Expression.Null); public readonly static Role<Expression> FalseRole = new Role<Expression>("False", null);
[Slot("ConditionRole")] [Slot("ConditionRole")]
public partial Expression Condition { get; set; } public partial Expression Condition { get; set; }

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

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: true)] [DecompilerAstNode(hasNullNode: true)]
public abstract partial class InterpolatedStringContent : AstNode public abstract partial class InterpolatedStringContent : AstNode
{ {
public new static readonly Role<InterpolatedStringContent> Role = new Role<InterpolatedStringContent>("InterpolatedStringContent", Syntax.InterpolatedStringContent.Null); public new static readonly Role<InterpolatedStringContent> Role = new Role<InterpolatedStringContent>("InterpolatedStringContent", null);
} }

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

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public static readonly Role<AttributeSection> AttributeRole = new Role<AttributeSection>("Attribute", null); public static readonly Role<AttributeSection> AttributeRole = new Role<AttributeSection>("Attribute", null);
public readonly static TokenRole AsyncModifierRole = new TokenRole("async"); public readonly static TokenRole AsyncModifierRole = new TokenRole("async");
public static readonly Role<AstNode> BodyRole = new Role<AstNode>("Body", AstNode.Null); public static readonly Role<AstNode> BodyRole = new Role<AstNode>("Body", null);
bool isAsync; bool isAsync;

20
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/QueryExpression.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>
@ -43,7 +45,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class QueryContinuationClause : QueryClause public partial class QueryContinuationClause : QueryClause
{ {
public static readonly Role<QueryExpression> PrecedingQueryRole = new Role<QueryExpression>("PrecedingQuery", QueryExpression.Null); public static readonly Role<QueryExpression> PrecedingQueryRole = new Role<QueryExpression>("PrecedingQuery", null);
public static readonly TokenRole IntoKeywordRole = new TokenRole("into"); public static readonly TokenRole IntoKeywordRole = new TokenRole("into");
[Slot("PrecedingQueryRole")] [Slot("PrecedingQueryRole")]
@ -63,7 +65,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole InKeywordRole = new TokenRole("in"); public static readonly TokenRole InKeywordRole = new TokenRole("in");
[Slot("Roles.Type")] [Slot("Roles.Type")]
public partial AstType Type { get; set; } public partial AstType? Type { get; set; }
[NameSlot("Roles.Identifier")] [NameSlot("Roles.Identifier")]
public partial string Identifier { get; set; } public partial string Identifier { get; set; }
@ -118,11 +120,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole InKeywordRole = new TokenRole("in"); public static readonly TokenRole InKeywordRole = new TokenRole("in");
public static readonly Role<Expression> InExpressionRole = Roles.Expression; public static readonly Role<Expression> InExpressionRole = Roles.Expression;
public static readonly TokenRole OnKeywordRole = new TokenRole("on"); public static readonly TokenRole OnKeywordRole = new TokenRole("on");
public static readonly Role<Expression> OnExpressionRole = new Role<Expression>("OnExpression", Expression.Null); public static readonly Role<Expression> OnExpressionRole = new Role<Expression>("OnExpression", null);
public static readonly TokenRole EqualsKeywordRole = new TokenRole("equals"); public static readonly TokenRole EqualsKeywordRole = new TokenRole("equals");
public static readonly Role<Expression> EqualsExpressionRole = new Role<Expression>("EqualsExpression", Expression.Null); public static readonly Role<Expression> EqualsExpressionRole = new Role<Expression>("EqualsExpression", null);
public static readonly TokenRole IntoKeywordRole = new TokenRole("into"); public static readonly TokenRole IntoKeywordRole = new TokenRole("into");
public static readonly Role<Identifier> IntoIdentifierRole = new Role<Identifier>("IntoIdentifier", Identifier.Null); public static readonly Role<Identifier> IntoIdentifierRole = new Role<Identifier>("IntoIdentifier", null);
// Derived from IntoIdentifier (which DoMatch already compares); exclude it to avoid a redundant compare. // Derived from IntoIdentifier (which DoMatch already compares); exclude it to avoid a redundant compare.
[ExcludeFromMatch] [ExcludeFromMatch]
@ -131,7 +133,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
[Slot("TypeRole")] [Slot("TypeRole")]
public partial AstType Type { get; set; } public partial AstType? Type { get; set; }
[NameSlot("JoinIdentifierRole")] [NameSlot("JoinIdentifierRole")]
public partial string JoinIdentifier { get; set; } public partial string JoinIdentifier { get; set; }
@ -145,7 +147,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[Slot("EqualsExpressionRole")] [Slot("EqualsExpressionRole")]
public partial Expression EqualsExpression { get; set; } public partial Expression EqualsExpression { get; set; }
[NameSlot("IntoIdentifierRole")] [NameSlot("IntoIdentifierRole", nullOnEmpty: true)]
public partial string IntoIdentifier { get; set; } public partial string IntoIdentifier { get; set; }
} }
@ -206,9 +208,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class QueryGroupClause : QueryClause public partial class QueryGroupClause : QueryClause
{ {
public static readonly TokenRole GroupKeywordRole = new TokenRole("group"); public static readonly TokenRole GroupKeywordRole = new TokenRole("group");
public static readonly Role<Expression> ProjectionRole = new Role<Expression>("Projection", Expression.Null); public static readonly Role<Expression> ProjectionRole = new Role<Expression>("Projection", null);
public static readonly TokenRole ByKeywordRole = new TokenRole("by"); public static readonly TokenRole ByKeywordRole = new TokenRole("by");
public static readonly Role<Expression> KeyRole = new Role<Expression>("Key", Expression.Null); public static readonly Role<Expression> KeyRole = new Role<Expression>("Key", null);
[Slot("ProjectionRole")] [Slot("ProjectionRole")]
public partial Expression Projection { get; set; } public partial Expression Projection { get; set; }

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

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class RecursivePatternExpression : Expression public partial class RecursivePatternExpression : Expression
{ {
public static readonly Role<Expression> SubPatternRole = new Role<Expression>("SubPattern", Syntax.Expression.Null); public static readonly Role<Expression> SubPatternRole = new Role<Expression>("SubPattern", null);
[Slot("Roles.Type")] [Slot("Roles.Type")]
public partial AstType? Type { get; set; } public partial AstType? Type { get; set; }

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

@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class StackAllocExpression : Expression public partial class StackAllocExpression : Expression
{ {
public readonly static TokenRole StackallocKeywordRole = new TokenRole("stackalloc"); public readonly static TokenRole StackallocKeywordRole = new TokenRole("stackalloc");
public readonly static Role<ArrayInitializerExpression> InitializerRole = new Role<ArrayInitializerExpression>("Initializer", ArrayInitializerExpression.Null); public readonly static Role<ArrayInitializerExpression> InitializerRole = new Role<ArrayInitializerExpression>("Initializer", null);
[Slot("Roles.Type")] [Slot("Roles.Type")]
public partial AstType? Type { get; set; } public partial AstType? Type { get; set; }

4
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs

@ -41,8 +41,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class SwitchExpressionSection : AstNode public partial class SwitchExpressionSection : AstNode
{ {
public static readonly Role<Expression> PatternRole = new Role<Expression>("Pattern", Expression.Null); public static readonly Role<Expression> PatternRole = new Role<Expression>("Pattern", null);
public static readonly Role<Expression> BodyRole = new Role<Expression>("Body", Expression.Null); public static readonly Role<Expression> BodyRole = new Role<Expression>("Body", null);
[Slot("PatternRole")] [Slot("PatternRole")]
public partial Expression Pattern { get; set; } public partial Expression Pattern { get; set; }

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

@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class FunctionPointerAstType : AstType public partial class FunctionPointerAstType : AstType
{ {
public static readonly TokenRole PointerRole = new TokenRole("*"); public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<AstType> CallingConventionRole = new Role<AstType>("CallConv", AstType.Null); public static readonly Role<AstType> CallingConventionRole = new Role<AstType>("CallConv", null);
public bool HasUnmanagedCallingConvention { get; set; } public bool HasUnmanagedCallingConvention { get; set; }

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs

@ -49,8 +49,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public override string ToString(CSharpFormattingOptions formattingOptions) public override string ToString(CSharpFormattingOptions formattingOptions)
{ {
if (IsNull)
return "Null";
return base.ToString(formattingOptions); return base.ToString(formattingOptions);
} }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public string AttributeTarget { public string AttributeTarget {
get { get {
return GetChildByRole(Roles.Identifier).Name; return GetChildByRole(Roles.Identifier)?.Name ?? string.Empty;
} }
set { set {
SetChildByRole(Roles.Identifier, Identifier.Create(value)); SetChildByRole(Roles.Identifier, Identifier.Create(value));

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs

@ -40,7 +40,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class NamespaceDeclaration : AstNode public partial class NamespaceDeclaration : AstNode
{ {
public static readonly Role<AstNode> MemberRole = SyntaxTree.MemberRole; public static readonly Role<AstNode> MemberRole = SyntaxTree.MemberRole;
public static readonly Role<AstType> NamespaceNameRole = new Role<AstType>("NamespaceName", AstType.Null); public static readonly Role<AstType> NamespaceNameRole = new Role<AstType>("NamespaceName", null);
public bool IsFileScoped { get; set; } public bool IsFileScoped { get; set; }

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class UsingAliasDeclaration : AstNode public partial class UsingAliasDeclaration : AstNode
{ {
public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); public static readonly TokenRole UsingKeywordRole = new TokenRole("using");
public static readonly Role<Identifier> AliasRole = new Role<Identifier>("Alias", Identifier.Null); public static readonly Role<Identifier> AliasRole = new Role<Identifier>("Alias", null);
public static readonly Role<AstType> ImportRole = UsingDeclaration.ImportRole; public static readonly Role<AstType> ImportRole = UsingDeclaration.ImportRole;
[NameSlot("AliasRole")] [NameSlot("AliasRole")]

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class UsingDeclaration : AstNode public partial class UsingDeclaration : AstNode
{ {
public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); public static readonly TokenRole UsingKeywordRole = new TokenRole("using");
public static readonly Role<AstType> ImportRole = new Role<AstType>("Import", AstType.Null); public static readonly Role<AstType> ImportRole = new Role<AstType>("Import", null);
[Slot("ImportRole")] [Slot("ImportRole")]
public partial AstType Import { get; set; } public partial AstType Import { get; set; }

4
ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs

@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static Identifier Create(string name, TextLocation location) public static Identifier Create(string name, TextLocation location)
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
return Identifier.Null; return new Identifier(string.Empty, location);
if (name[0] == '@') if (name[0] == '@')
return new Identifier(name.Substring(1), new TextLocation(location.Line, location.Column + 1)) { IsVerbatim = true }; return new Identifier(name.Substring(1), new TextLocation(location.Line, location.Column + 1)) { IsVerbatim = true };
else else
@ -116,7 +116,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static Identifier Create(string name, TextLocation location, bool isVerbatim) public static Identifier Create(string name, TextLocation location, bool isVerbatim)
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
return Identifier.Null; return new Identifier(string.Empty, location);
if (isVerbatim) if (isVerbatim)
return new Identifier(name, location) { IsVerbatim = true }; return new Identifier(name, location) { IsVerbatim = true };

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

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class MemberType : AstType public partial class MemberType : AstType
{ {
public static readonly Role<AstType> TargetRole = new Role<AstType>("Target", AstType.Null); public static readonly Role<AstType> TargetRole = new Role<AstType>("Target", null);
bool isDoubleColon; bool isDoubleColon;

2
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
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 && !other.IsNull; return other != null;
} }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs

@ -26,8 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
/// </summary> /// </summary>
public interface INode public interface INode
{ {
bool IsNull { get; }
bool DoMatch(INode other, Match match); bool DoMatch(INode other, Match match);
/// <summary> /// <summary>

2
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode other, Match match)
{ {
if (other == null || other.IsNull) if (other == null)
return true; return true;
else else
return childNode.DoMatch(other, match); return childNode.DoMatch(other, match);

4
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs

@ -48,10 +48,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
} }
} }
bool INode.IsNull {
get { return false; }
}
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)

2
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs

@ -62,7 +62,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
public override bool DoMatch(INode other, Match match) public override bool DoMatch(INode other, Match match)
{ {
if (other == null || other.IsNull) if (other == null)
return this.MinCount <= 0; return this.MinCount <= 0;
else else
return this.MaxCount >= 1 && childNode.DoMatch(other, match); return this.MaxCount >= 1 && childNode.DoMatch(other, match);

26
ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs

@ -31,22 +31,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly Role<AstNode> Root = AstNode.RootRole; public static readonly Role<AstNode> Root = AstNode.RootRole;
// some pre defined constants for common roles // some pre defined constants for common roles
public static readonly Role<Identifier> Identifier = new Role<Identifier>("Identifier", Syntax.Identifier.Null); public static readonly Role<Identifier> Identifier = new Role<Identifier>("Identifier", null);
public static readonly Role<BlockStatement> Body = new Role<BlockStatement>("Body", BlockStatement.Null); public static readonly Role<BlockStatement> Body = new Role<BlockStatement>("Body", null);
public static readonly Role<ParameterDeclaration> Parameter = new Role<ParameterDeclaration>("Parameter", null); public static readonly Role<ParameterDeclaration> Parameter = new Role<ParameterDeclaration>("Parameter", null);
public static readonly Role<Expression> Argument = new Role<Expression>("Argument", Syntax.Expression.Null); public static readonly Role<Expression> Argument = new Role<Expression>("Argument", null);
public static readonly Role<AstType> Type = new Role<AstType>("Type", AstType.Null); public static readonly Role<AstType> Type = new Role<AstType>("Type", null);
public static readonly Role<Expression> Expression = new Role<Expression>("Expression", Syntax.Expression.Null); public static readonly Role<Expression> Expression = new Role<Expression>("Expression", null);
public static readonly Role<Expression> TargetExpression = new Role<Expression>("Target", Syntax.Expression.Null); public static readonly Role<Expression> TargetExpression = new Role<Expression>("Target", null);
public readonly static Role<Expression> Condition = new Role<Expression>("Condition", Syntax.Expression.Null); public readonly static Role<Expression> Condition = new Role<Expression>("Condition", null);
public static readonly Role<TypeParameterDeclaration> TypeParameter = new Role<TypeParameterDeclaration>("TypeParameter", null); public static readonly Role<TypeParameterDeclaration> TypeParameter = new Role<TypeParameterDeclaration>("TypeParameter", null);
public static readonly Role<AstType> TypeArgument = new Role<AstType>("TypeArgument", AstType.Null); public static readonly Role<AstType> TypeArgument = new Role<AstType>("TypeArgument", null);
public readonly static Role<Constraint> Constraint = new Role<Constraint>("Constraint", null); public readonly static Role<Constraint> Constraint = new Role<Constraint>("Constraint", null);
public static readonly Role<VariableInitializer> Variable = new Role<VariableInitializer>("Variable", VariableInitializer.Null); public static readonly Role<VariableInitializer> Variable = new Role<VariableInitializer>("Variable", null);
public static readonly Role<Statement> EmbeddedStatement = new Role<Statement>("EmbeddedStatement", Statement.Null); public static readonly Role<Statement> EmbeddedStatement = new Role<Statement>("EmbeddedStatement", null);
public readonly static Role<EntityDeclaration> TypeMemberRole = new Role<EntityDeclaration>("TypeMember", null); public readonly static Role<EntityDeclaration> TypeMemberRole = new Role<EntityDeclaration>("TypeMember", null);
public static readonly Role<VariableDesignation> VariableDesignationRole = new Role<VariableDesignation>("VariableDesignation", VariableDesignation.Null); public static readonly Role<VariableDesignation> VariableDesignationRole = new Role<VariableDesignation>("VariableDesignation", null);
// some pre defined constants for most used punctuation // some pre defined constants for most used punctuation
public static readonly TokenRole LPar = new TokenRole("("); public static readonly TokenRole LPar = new TokenRole("(");
@ -67,12 +67,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly Role<Comment> Comment = new Role<Comment>("Comment", null); public static readonly Role<Comment> Comment = new Role<Comment>("Comment", null);
public static readonly Role<PreProcessorDirective> PreProcessorDirective = new Role<PreProcessorDirective>("PreProcessorDirective", null); public static readonly Role<PreProcessorDirective> PreProcessorDirective = new Role<PreProcessorDirective>("PreProcessorDirective", null);
public readonly static Role<AstType> BaseType = new Role<AstType>("BaseType", AstType.Null); public readonly static Role<AstType> BaseType = new Role<AstType>("BaseType", null);
public static readonly Role<Attribute> Attribute = new Role<Attribute>("Attribute", null); public static readonly Role<Attribute> Attribute = new Role<Attribute>("Attribute", null);
public readonly static TokenRole WhereKeyword = new TokenRole("where"); public readonly static TokenRole WhereKeyword = new TokenRole("where");
public readonly static Role<SimpleType> ConstraintTypeParameter = new Role<SimpleType>("TypeParameter", SimpleType.Null); public readonly static Role<SimpleType> ConstraintTypeParameter = new Role<SimpleType>("TypeParameter", null);
public readonly static TokenRole DelegateKeyword = new TokenRole("delegate"); public readonly static TokenRole DelegateKeyword = new TokenRole("delegate");
public static readonly TokenRole ExternKeyword = new TokenRole("extern"); public static readonly TokenRole ExternKeyword = new TokenRole("extern");
public static readonly TokenRole AliasKeyword = new TokenRole("alias"); public static readonly TokenRole AliasKeyword = new TokenRole("alias");

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

@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
} }
[NameSlot("Roles.Identifier")] [NameSlot("Roles.Identifier", nullOnEmpty: true)]
public partial string Identifier { get; set; } public partial string Identifier { get; set; }
[Slot("Roles.TypeArgument")] [Slot("Roles.TypeArgument")]

2
ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: true, hasPatternPlaceholder: true)] [DecompilerAstNode(hasNullNode: true, hasPatternPlaceholder: true)]
public partial class BlockStatement : Statement, IEnumerable<Statement> public partial class BlockStatement : Statement, IEnumerable<Statement>
{ {
public static readonly Role<Statement> StatementRole = new Role<Statement>("Statement", Statement.Null); public static readonly Role<Statement> StatementRole = new Role<Statement>("Statement", null);
[Slot("StatementRole")] [Slot("StatementRole")]
public partial AstNodeCollection<Statement> Statements { get; } public partial AstNodeCollection<Statement> Statements { get; }

4
ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs

@ -35,8 +35,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class ForStatement : Statement public partial class ForStatement : Statement
{ {
public static readonly TokenRole ForKeywordRole = new TokenRole("for"); public static readonly TokenRole ForKeywordRole = new TokenRole("for");
public readonly static Role<Statement> InitializerRole = new Role<Statement>("Initializer", Statement.Null); public readonly static Role<Statement> InitializerRole = new Role<Statement>("Initializer", null);
public readonly static Role<Statement> IteratorRole = new Role<Statement>("Iterator", Statement.Null); public readonly static Role<Statement> IteratorRole = new Role<Statement>("Iterator", null);
/// <summary> /// <summary>
/// Gets the list of initializer statements. /// Gets the list of initializer statements.

4
ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs

@ -36,9 +36,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public readonly static TokenRole IfKeywordRole = new TokenRole("if"); public readonly static TokenRole IfKeywordRole = new TokenRole("if");
public readonly static Role<Expression> ConditionRole = Roles.Condition; public readonly static Role<Expression> ConditionRole = Roles.Condition;
public readonly static Role<Statement> TrueRole = new Role<Statement>("True", Statement.Null); public readonly static Role<Statement> TrueRole = new Role<Statement>("True", null);
public readonly static TokenRole ElseKeywordRole = new TokenRole("else"); public readonly static TokenRole ElseKeywordRole = new TokenRole("else");
public readonly static Role<Statement> FalseRole = new Role<Statement>("False", Statement.Null); public readonly static Role<Statement> FalseRole = new Role<Statement>("False", null);
[Slot("ConditionRole")] [Slot("ConditionRole")]
public partial Expression Condition { get; set; } public partial Expression Condition { get; set; }

6
ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs

@ -35,10 +35,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial class TryCatchStatement : Statement public partial class TryCatchStatement : Statement
{ {
public static readonly TokenRole TryKeywordRole = new TokenRole("try"); public static readonly TokenRole TryKeywordRole = new TokenRole("try");
public static readonly Role<BlockStatement> TryBlockRole = new Role<BlockStatement>("TryBlock", BlockStatement.Null); public static readonly Role<BlockStatement> TryBlockRole = new Role<BlockStatement>("TryBlock", null);
public static readonly Role<CatchClause> CatchClauseRole = new Role<CatchClause>("CatchClause", CatchClause.Null); public static readonly Role<CatchClause> CatchClauseRole = new Role<CatchClause>("CatchClause", null);
public static readonly TokenRole FinallyKeywordRole = new TokenRole("finally"); public static readonly TokenRole FinallyKeywordRole = new TokenRole("finally");
public static readonly Role<BlockStatement> FinallyBlockRole = new Role<BlockStatement>("FinallyBlock", BlockStatement.Null); public static readonly Role<BlockStatement> FinallyBlockRole = new Role<BlockStatement>("FinallyBlock", null);
[Slot("TryBlockRole")] [Slot("TryBlockRole")]
public partial BlockStatement TryBlock { get; set; } public partial BlockStatement TryBlock { get; set; }

2
ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); public static readonly TokenRole UsingKeywordRole = new TokenRole("using");
public static readonly TokenRole AwaitRole = UnaryOperatorExpression.AwaitRole; public static readonly TokenRole AwaitRole = UnaryOperatorExpression.AwaitRole;
public static readonly Role<AstNode> ResourceAcquisitionRole = new Role<AstNode>("ResourceAcquisition", AstNode.Null); public static readonly Role<AstNode> ResourceAcquisitionRole = new Role<AstNode>("ResourceAcquisition", null);
public bool IsAsync { get; set; } public bool IsAsync { get; set; }

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

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class SyntaxTree : AstNode public partial class SyntaxTree : AstNode
{ {
public static readonly Role<AstNode> MemberRole = new Role<AstNode>("Member", AstNode.Null); public static readonly Role<AstNode> MemberRole = new Role<AstNode>("Member", null);
[Slot("MemberRole")] [Slot("MemberRole")]
public partial AstNodeCollection<AstNode> Members { get; } public partial AstNodeCollection<AstNode> Members { get; }

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

@ -24,7 +24,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class TupleAstType : AstType public partial class TupleAstType : AstType
{ {
public static readonly Role<TupleTypeElement> ElementRole = new Role<TupleTypeElement>("Element", TupleTypeElement.Null); public static readonly Role<TupleTypeElement> ElementRole = new Role<TupleTypeElement>("Element", null);
[Slot("ElementRole")] [Slot("ElementRole")]
public partial AstNodeCollection<TupleTypeElement> Elements { get; } public partial AstNodeCollection<TupleTypeElement> Elements { get; }

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs

@ -63,7 +63,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
public override Identifier NameToken { public override Identifier NameToken {
get { return Identifier.Null; } get { return null!; }
set { } set { }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class ConstructorDeclaration : EntityDeclaration public partial class ConstructorDeclaration : EntityDeclaration
{ {
public static readonly Role<ConstructorInitializer> InitializerRole = new Role<ConstructorInitializer>("Initializer", ConstructorInitializer.Null); public static readonly Role<ConstructorInitializer> InitializerRole = new Role<ConstructorInitializer>("Initializer", null);
public override SymbolKind SymbolKind { public override SymbolKind SymbolKind {
get { return SymbolKind.Constructor; } get { return SymbolKind.Constructor; }

4
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
// Identity marker for modifier keywords in the output (modifiers are scalars, not child nodes); // Identity marker for modifier keywords in the output (modifiers are scalars, not child nodes);
// the token writer uses it to make the 'override' modifier a go-to-definition reference. // the token writer uses it to make the 'override' modifier a go-to-definition reference.
public static readonly TokenRole ModifierRole = new TokenRole("modifier"); public static readonly TokenRole ModifierRole = new TokenRole("modifier");
public static readonly Role<AstType> PrivateImplementationTypeRole = new Role<AstType>("PrivateImplementationType", AstType.Null); public static readonly Role<AstType> PrivateImplementationTypeRole = new Role<AstType>("PrivateImplementationType", null);
public abstract SymbolKind SymbolKind { get; } public abstract SymbolKind SymbolKind { get; }
@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public virtual string Name { public virtual string Name {
get { get {
return GetChildByRole(Roles.Identifier).Name; return GetChildByRole(Roles.Identifier)?.Name ?? string.Empty;
} }
set { set {
SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty)); SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty));

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[DecompilerAstNode(hasNullNode: false)] [DecompilerAstNode(hasNullNode: false)]
public partial class EnumMemberDeclaration : EntityDeclaration public partial class EnumMemberDeclaration : EntityDeclaration
{ {
public static readonly Role<Expression> InitializerRole = new Role<Expression>("Initializer", Expression.Null); public static readonly Role<Expression> InitializerRole = new Role<Expression>("Initializer", null);
public override SymbolKind SymbolKind { public override SymbolKind SymbolKind {
get { return SymbolKind.Field; } get { return SymbolKind.Field; }

6
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs

@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override Identifier NameToken { public override Identifier NameToken {
get { return Identifier.Null; } get { return null!; }
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }
} }
@ -79,8 +79,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole AddKeywordRole = new TokenRole("add"); public static readonly TokenRole AddKeywordRole = new TokenRole("add");
public static readonly TokenRole RemoveKeywordRole = new TokenRole("remove"); public static readonly TokenRole RemoveKeywordRole = new TokenRole("remove");
public static readonly Role<Accessor> AddAccessorRole = new Role<Accessor>("AddAccessor", Accessor.Null); public static readonly Role<Accessor> AddAccessorRole = new Role<Accessor>("AddAccessor", null);
public static readonly Role<Accessor> RemoveAccessorRole = new Role<Accessor>("RemoveAccessor", Accessor.Null); public static readonly Role<Accessor> RemoveAccessorRole = new Role<Accessor>("RemoveAccessor", null);
public override SymbolKind SymbolKind { public override SymbolKind SymbolKind {
get { return SymbolKind.Event; } get { return SymbolKind.Event; }

15
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs

@ -49,20 +49,5 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public ExtensionDeclaration() public ExtensionDeclaration()
{ {
} }
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitExtensionDeclaration(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitExtensionDeclaration(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitExtensionDeclaration(this, data);
}
} }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs

@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override Identifier NameToken { public override Identifier NameToken {
get { return Identifier.Null; } get { return null!; }
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }
} }

4
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs

@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole ThisKeywordRole = new TokenRole("this"); public static readonly TokenRole ThisKeywordRole = new TokenRole("this");
public static readonly Role<Accessor> GetterRole = PropertyDeclaration.GetterRole; public static readonly Role<Accessor> GetterRole = PropertyDeclaration.GetterRole;
public static readonly Role<Accessor> SetterRole = PropertyDeclaration.SetterRole; public static readonly Role<Accessor> SetterRole = PropertyDeclaration.SetterRole;
public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", Expression.Null); public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", null);
public override SymbolKind SymbolKind { public override SymbolKind SymbolKind {
get { return SymbolKind.Indexer; } get { return SymbolKind.Indexer; }
@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override Identifier NameToken { public override Identifier NameToken {
get { return Identifier.Null; } get { return null!; }
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs

@ -324,7 +324,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override Identifier NameToken { public override Identifier NameToken {
get { return Identifier.Null; } get { return null!; }
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs

@ -86,7 +86,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
[Slot("Roles.Type")] [Slot("Roles.Type")]
public partial AstType? Type { get; set; } public partial AstType? Type { get; set; }
[NameSlot("Roles.Identifier")] [NameSlot("Roles.Identifier", nullOnEmpty: true)]
public partial string Name { get; set; } public partial string Name { get; set; }
[Slot("Roles.Expression")] [Slot("Roles.Expression")]

8
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs

@ -44,9 +44,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole GetKeywordRole = new TokenRole("get"); public static readonly TokenRole GetKeywordRole = new TokenRole("get");
public static readonly TokenRole SetKeywordRole = new TokenRole("set"); public static readonly TokenRole SetKeywordRole = new TokenRole("set");
public static readonly TokenRole InitKeywordRole = new TokenRole("init"); public static readonly TokenRole InitKeywordRole = new TokenRole("init");
public static readonly Role<Accessor> GetterRole = new Role<Accessor>("Getter", Accessor.Null); public static readonly Role<Accessor> GetterRole = new Role<Accessor>("Getter", null);
public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", Accessor.Null); public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", null);
public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", Expression.Null); public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", null);
public override SymbolKind SymbolKind { public override SymbolKind SymbolKind {
get { return SymbolKind.Property; } get { return SymbolKind.Property; }
@ -75,7 +75,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public partial Accessor? Setter { get; set; } public partial Accessor? Setter { get; set; }
[Slot("Roles.Expression")] [Slot("Roles.Expression")]
public partial Expression Initializer { get; set; } public partial Expression? Initializer { get; set; }
[Slot("ExpressionBodyRole")] [Slot("ExpressionBodyRole")]
public partial Expression? ExpressionBody { get; set; } public partial Expression? ExpressionBody { get; set; }

4
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -2081,14 +2081,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
else else
{ {
return BlockStatement.Null; return null;
} }
} }
Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute) Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute)
{ {
if (accessor == null) if (accessor == null)
return Accessor.Null; return null;
Accessor decl = new Accessor(); Accessor decl = new Accessor();
if (ShowAttributes) if (ShowAttributes)
{ {

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

@ -393,7 +393,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
// We cannot use checked/unchecked for top-level-expressions. // We cannot use checked/unchecked for top-level-expressions.
} }
else if (expr.Role.IsValid(Expression.Null)) else if (expr.Role is Role<Expression> || expr.Role is Role<AstNode>)
{ {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3) // We use '<' so that expressions are introduced on the deepest level possible (goal 3)
var costIfWrapWithChecked = result.CostInCheckedContext.WrapInCheckedExpr(); var costIfWrapWithChecked = result.CostInCheckedContext.WrapInCheckedExpr();

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

@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
var memberNames = typeDecl.Members.Select(m => { var memberNames = typeDecl.Members.Select(m => {
var type = m.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); var type = m.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole);
return type.IsNull ? m.Name : type + "." + m.Name; return type is null ? m.Name : type + "." + m.Name;
}).ToHashSet(); }).ToHashSet();
// memberNames does not include fields or non-custom events because those // memberNames does not include fields or non-custom events because those
// don't have a single name, but a list of VariableInitializers. // don't have a single name, but a list of VariableInitializers.

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

@ -258,6 +258,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// lambda, so this match always succeeds. // lambda, so this match always succeeds.
bool matched = MatchSimpleLambda(lambda, out parameter, out orderExpression); bool matched = MatchSimpleLambda(lambda, out parameter, out orderExpression);
Debug.Assert(matched); Debug.Assert(matched);
Debug.Assert(orderExpression != null);
} }
// The chain was validated and every iteration re-matched a simple lambda, // The chain was validated and every iteration re-matched a simple lambda,
// so the final range-variable parameter is set. // so the final range-variable parameter is set.

4
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -97,8 +97,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
// transform "*(ptr + int)" to "ptr[int]" // transform "*(ptr + int)" to "ptr[int]"
IndexerExpression indexer = new IndexerExpression(); IndexerExpression indexer = new IndexerExpression();
indexer.Target = bop.Left.Detach(); indexer.Target = bop.Left!.Detach();
indexer.Arguments.Add(bop.Right.Detach()); indexer.Arguments.Add(bop.Right!.Detach());
indexer.CopyAnnotationsFrom(unaryOperatorExpression); indexer.CopyAnnotationsFrom(unaryOperatorExpression);
indexer.CopyAnnotationsFrom(bop); indexer.CopyAnnotationsFrom(bop);
unaryOperatorExpression.ReplaceWith(indexer); unaryOperatorExpression.ReplaceWith(indexer);

4
ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs

@ -93,7 +93,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
void DoTransform(Statement? statement, Statement parent) void DoTransform(Statement? statement, Statement parent)
{ {
if (statement is null || statement.IsNull) if (statement is null)
return; return;
if (context.Settings.AlwaysUseBraces) if (context.Settings.AlwaysUseBraces)
{ {
@ -122,8 +122,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
static void InsertBlock(Statement statement) static void InsertBlock(Statement statement)
{ {
if (statement.IsNull)
return;
if (!(statement is BlockStatement)) if (!(statement is BlockStatement))
{ {
var b = new BlockStatement(); var b = new BlockStatement();

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

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (rhs is BinaryOperatorExpression binary && assignment.Operator == AssignmentOperatorType.Assign) if (rhs is BinaryOperatorExpression binary && assignment.Operator == AssignmentOperatorType.Assign)
{ {
if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left) if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left)
&& IsImplicitlyConvertible(binary.Right, expectedType)) && binary.Right != null && IsImplicitlyConvertible(binary.Right, expectedType))
{ {
assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator); assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
if (assignment.Operator != AssignmentOperatorType.Assign) if (assignment.Operator != AssignmentOperatorType.Assign)

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

@ -546,7 +546,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
break; break;
case PropertyDeclaration pd: case PropertyDeclaration pd:
Debug.Assert(pd.IsAutomaticProperty); Debug.Assert(pd.IsAutomaticProperty);
if (pd.Initializer.IsNull) if (pd.Initializer is null)
{ {
pd.Initializer = initializer.Detach(); pd.Initializer = initializer.Detach();
} }

2
ICSharpCode.Decompiler/IL/ILAmbience.cs

@ -400,7 +400,7 @@ namespace ICSharpCode.Decompiler.IL
builder.Append(type.CallingConvention.ToILSyntax()); builder.Append(type.CallingConvention.ToILSyntax());
builder.Append(' '); builder.Append(' ');
} }
type.ReturnType.AcceptVisitor(this); type.ReturnType?.AcceptVisitor(this);
builder.Append(" *("); builder.Append(" *(");
bool first = true; bool first = true;
foreach (var p in type.ParameterTypes) foreach (var p in type.ParameterTypes)

2
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -300,7 +300,7 @@ namespace ICSharpCode.Decompiler
// Attach member reference to token only if there's no identifier in the current node. // Attach member reference to token only if there's no identifier in the current node.
var member = GetCurrentMemberReference(); var member = GetCurrentMemberReference();
var node = nodeStack.Peek(); var node = nodeStack.Peek();
if (member != null && node.GetChildByRole(Roles.Identifier).IsNull) if (member != null && node.GetChildByRole(Roles.Identifier) is null)
{ {
switch (member) switch (member)
{ {

2
ILSpy/Languages/CSharpLanguage.cs

@ -627,7 +627,7 @@ namespace ICSharpCode.ILSpy.Languages
} }
break; break;
case PropertyDeclaration pd: case PropertyDeclaration pd:
if (pd.Initializer.IsNull) if (pd.Initializer is null)
{ {
pd.Remove(); pd.Remove();
removedSymbols.Add(pd.GetSymbol()); removedSymbols.Add(pd.GetSymbol());

Loading…
Cancel
Save