From 9aa0ddd37b9ac2abd88a0a87a0b0e92f1fdf0d73 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 16 Jun 2026 23:40:18 +0200 Subject: [PATCH] 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 --- .../DecompilerSyntaxTreeGenerator.cs | 59 +++---------------- .../CSharp/CSharpDecompiler.cs | 6 +- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 6 +- .../CSharp/OutputVisitor/CSharpAmbience.cs | 4 +- .../OutputVisitor/CSharpOutputVisitor.cs | 48 ++++++++------- .../InsertMissingTokensDecorator.cs | 2 +- .../CSharp/SequencePointBuilder.cs | 2 +- .../CSharp/Syntax/AstNode.cs | 36 ++++------- .../CSharp/Syntax/AstNodeCollection.cs | 4 +- .../CSharp/Syntax/DepthFirstAstVisitor.cs | 26 -------- .../CSharp/Syntax/DocumentationReference.cs | 4 +- .../Expressions/ArrayCreateExpression.cs | 4 +- .../Expressions/BinaryOperatorExpression.cs | 12 ++-- .../Expressions/ConditionalExpression.cs | 4 +- .../InterpolatedStringExpression.cs | 2 +- .../Syntax/Expressions/LambdaExpression.cs | 2 +- .../Syntax/Expressions/QueryExpression.cs | 20 ++++--- .../Expressions/RecursivePatternExpression.cs | 2 +- .../Expressions/StackAllocExpression.cs | 2 +- .../Syntax/Expressions/SwitchExpression.cs | 4 +- .../CSharp/Syntax/FunctionPointerAstType.cs | 2 +- .../CSharp/Syntax/GeneralScope/Attribute.cs | 2 - .../Syntax/GeneralScope/AttributeSection.cs | 2 +- .../GeneralScope/NamespaceDeclaration.cs | 2 +- .../GeneralScope/UsingAliasDeclaration.cs | 2 +- .../Syntax/GeneralScope/UsingDeclaration.cs | 2 +- .../CSharp/Syntax/Identifier.cs | 4 +- .../CSharp/Syntax/MemberType.cs | 2 +- .../CSharp/Syntax/PatternMatching/AnyNode.cs | 2 +- .../CSharp/Syntax/PatternMatching/INode.cs | 2 - .../Syntax/PatternMatching/OptionalNode.cs | 2 +- .../CSharp/Syntax/PatternMatching/Pattern.cs | 4 -- .../CSharp/Syntax/PatternMatching/Repeat.cs | 2 +- ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs | 26 ++++---- .../CSharp/Syntax/SimpleType.cs | 2 +- .../Syntax/Statements/BlockStatement.cs | 2 +- .../CSharp/Syntax/Statements/ForStatement.cs | 4 +- .../Syntax/Statements/IfElseStatement.cs | 4 +- .../Syntax/Statements/TryCatchStatement.cs | 6 +- .../Syntax/Statements/UsingStatement.cs | 2 +- .../CSharp/Syntax/SyntaxTree.cs | 2 +- .../CSharp/Syntax/TupleAstType.cs | 2 +- .../CSharp/Syntax/TypeMembers/Accessor.cs | 2 +- .../TypeMembers/ConstructorDeclaration.cs | 2 +- .../Syntax/TypeMembers/EntityDeclaration.cs | 4 +- .../TypeMembers/EnumMemberDeclaration.cs | 2 +- .../Syntax/TypeMembers/EventDeclaration.cs | 6 +- .../TypeMembers/ExtensionDeclaration.cs | 15 ----- .../Syntax/TypeMembers/FieldDeclaration.cs | 2 +- .../Syntax/TypeMembers/IndexerDeclaration.cs | 4 +- .../Syntax/TypeMembers/OperatorDeclaration.cs | 2 +- .../TypeMembers/ParameterDeclaration.cs | 2 +- .../Syntax/TypeMembers/PropertyDeclaration.cs | 8 +-- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 4 +- .../CSharp/Transforms/AddCheckedBlocks.cs | 2 +- .../CSharp/Transforms/FixNameCollisions.cs | 2 +- .../Transforms/IntroduceQueryExpressions.cs | 1 + .../Transforms/IntroduceUnsafeModifier.cs | 4 +- .../Transforms/NormalizeBlockStatements.cs | 4 +- .../CSharp/Transforms/PrettifyAssignments.cs | 2 +- ...ransformFieldAndConstructorInitializers.cs | 2 +- ICSharpCode.Decompiler/IL/ILAmbience.cs | 2 +- .../Output/TextTokenWriter.cs | 2 +- ILSpy/Languages/CSharpLanguage.cs | 2 +- 64 files changed, 150 insertions(+), 255 deletions(-) diff --git a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs index 1866ebd28..9e9ac19ce 100644 --- a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs +++ b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.Generators; [Generator] 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 // trailing "Role" removed (e.g. "Roles.EmbeddedStatement" -> "EmbeddedStatement", "LeftRole" -> @@ -80,7 +80,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator if (!excludeName) membersToMatch.Add(("Name", "String", 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()) @@ -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, NeedsNullNode: (bool)attribute.ConstructorArguments[0].Value!, NeedsPatternPlaceholder: (bool)attribute.ConstructorArguments[1].Value!, @@ -184,51 +184,6 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator builder.AppendLine($"partial class {source.NodeName}"); 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(IAstVisitor visitor) - {{ - return visitor.VisitNullNode(this); - }} - - public override S AcceptVisitor(IAstVisitor 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) { // 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) {{ @@ -307,7 +262,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator { 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) { @@ -366,7 +321,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator builder.AppendLine($"\t{type}? {field};"); builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}{type}{(isNullable ? "?" : "")} {name}"); 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}"); } @@ -514,7 +469,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;"); 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(); WriteInterface("IAstVisitor", "void", ""); diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index d9ef64ae0..e711994a3 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1406,7 +1406,7 @@ namespace ICSharpCode.Decompiler.CSharp EntityDeclaration memberDecl, IMethod method, 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 } @@ -1427,7 +1427,7 @@ namespace ICSharpCode.Decompiler.CSharp if (m == null || m.DeclaringType.Kind != TypeKind.Interface) continue; var methodDecl = new MethodDeclaration(); - methodDecl.ReturnType = memberDecl.ReturnType.Clone(); + methodDecl.ReturnType = memberDecl.ReturnType?.Clone(); methodDecl.PrivateImplementationType = astBuilder.ConvertType(m.DeclaringType); methodDecl.Name = m.Name; methodDecl.TypeParameters.AddRange(memberDecl.GetChildrenByRole(Roles.TypeParameter) @@ -2041,7 +2041,7 @@ namespace ICSharpCode.Decompiler.CSharp } var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); - var body = BlockStatement.Null; + BlockStatement body = new BlockStatement(); MethodBodyBlock methodBody; try { diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 33bc27aba..0e81ae130 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/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) { - result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, Expression.Null) + result = new BinaryOperatorExpression(null, BinaryOperatorType.Range, null) .WithRR(new MemberResolveResult(null, method.AccessorOwner ?? method)); return true; } 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)); return true; } 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)); return true; } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index d4a93a292..5917fa647 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { var rt = node.GetChildByRole(Roles.Type); - if (!rt.IsNull) + if (rt is not null) { rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); writer.Space(); @@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { var rt = node.GetChildByRole(Roles.Type); - if (!rt.IsNull) + if (rt is not null) { writer.Space(); writer.WriteToken(Roles.Colon, ":"); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 9a9579a10..b40a52838 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -502,7 +502,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor /// protected virtual void WriteEmbeddedStatement(Statement embeddedStatement, NewLinePlacement nlp = NewLinePlacement.NewLine) { - if (embeddedStatement.IsNull) + if (embeddedStatement is null) { NewLine(); return; @@ -531,7 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style, bool newLine = true) { - if (body is null || body.IsNull) + if (body is null) { Semicolon(); } @@ -552,7 +552,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor protected virtual void WritePrivateImplementationType(AstType privateImplementationType) { - if (privateImplementationType is not null && !privateImplementationType.IsNull) + if (privateImplementationType is not null) { privateImplementationType.AcceptVisitor(this); WriteToken(Roles.Dot); @@ -611,7 +611,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(arrayCreateExpression); WriteKeyword(ArrayCreateExpression.NewKeywordRole); - arrayCreateExpression.Type.AcceptVisitor(this); + arrayCreateExpression.Type?.AcceptVisitor(this); if (arrayCreateExpression.Arguments.Count > 0) { WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); @@ -765,7 +765,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { StartNode(binaryOperatorExpression); - binaryOperatorExpression.Left.AcceptVisitor(this); + binaryOperatorExpression.Left?.AcceptVisitor(this); bool spacePolicy; switch (binaryOperatorExpression.Operator) { @@ -823,7 +823,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(tokenRole); } Space(spacePolicy); - binaryOperatorExpression.Right.AcceptVisitor(this); + binaryOperatorExpression.Right?.AcceptVisitor(this); EndNode(binaryOperatorExpression); } @@ -1361,7 +1361,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor StartNode(queryFromClause); WriteKeyword(QueryFromClause.FromKeywordRole); Space(); - queryFromClause.Type.AcceptVisitor(this); + queryFromClause.Type?.AcceptVisitor(this); Space(); WriteIdentifier(queryFromClause.IdentifierToken); Space(); @@ -1397,7 +1397,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(queryJoinClause); WriteKeyword(QueryJoinClause.JoinKeywordRole); - queryJoinClause.Type.AcceptVisitor(this); + queryJoinClause.Type?.AcceptVisitor(this); Space(); WriteIdentifier(queryJoinClause.JoinIdentifierToken); Space(); @@ -1523,7 +1523,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(delegateDeclaration.Attributes); WriteModifiers(delegateDeclaration.Modifiers); WriteKeyword(Roles.DelegateKeyword); - delegateDeclaration.ReturnType.AcceptVisitor(this); + delegateDeclaration.ReturnType?.AcceptVisitor(this); Space(); WriteIdentifier(delegateDeclaration.NameToken); WriteTypeParameters(delegateDeclaration.TypeParameters); @@ -2416,7 +2416,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(eventDeclaration.Attributes); WriteModifiers(eventDeclaration.Modifiers); WriteKeyword(EventDeclaration.EventKeywordRole); - eventDeclaration.ReturnType.AcceptVisitor(this); + eventDeclaration.ReturnType?.AcceptVisitor(this); Space(); WriteCommaSeparatedList(eventDeclaration.Variables); Semicolon(); @@ -2429,7 +2429,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(customEventDeclaration.Attributes); WriteModifiers(customEventDeclaration.Modifiers); WriteKeyword(CustomEventDeclaration.EventKeywordRole); - customEventDeclaration.ReturnType.AcceptVisitor(this); + customEventDeclaration.ReturnType?.AcceptVisitor(this); Space(); WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); WriteIdentifier(customEventDeclaration.NameToken); @@ -2452,7 +2452,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor StartNode(fieldDeclaration); WriteAttributes(fieldDeclaration.Attributes); WriteModifiers(fieldDeclaration.Modifiers); - fieldDeclaration.ReturnType.AcceptVisitor(this); + fieldDeclaration.ReturnType?.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fieldDeclaration.Variables); Semicolon(); @@ -2466,7 +2466,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteModifiers(fixedFieldDeclaration.Modifiers); WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); Space(); - fixedFieldDeclaration.ReturnType.AcceptVisitor(this); + fixedFieldDeclaration.ReturnType?.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fixedFieldDeclaration.Variables); Semicolon(); @@ -2477,7 +2477,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(fixedVariableInitializer); WriteIdentifier(fixedVariableInitializer.NameToken); - if (!fixedVariableInitializer.CountExpression.IsNull) + if (fixedVariableInitializer.CountExpression is not null) { WriteToken(Roles.LBracket); Space(policy.SpacesWithinBrackets); @@ -2493,7 +2493,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor StartNode(indexerDeclaration); WriteAttributes(indexerDeclaration.Attributes); WriteModifiers(indexerDeclaration.Modifiers); - indexerDeclaration.ReturnType.AcceptVisitor(this); + indexerDeclaration.ReturnType?.AcceptVisitor(this); Space(); WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); WriteKeyword(IndexerDeclaration.ThisKeywordRole); @@ -2538,7 +2538,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor StartNode(methodDeclaration); WriteAttributes(methodDeclaration.Attributes); WriteModifiers(methodDeclaration.Modifiers); - methodDeclaration.ReturnType.AcceptVisitor(this); + methodDeclaration.ReturnType?.AcceptVisitor(this); Space(); WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); WriteIdentifier(methodDeclaration.NameToken); @@ -2568,7 +2568,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } else { - operatorDeclaration.ReturnType.AcceptVisitor(this); + operatorDeclaration.ReturnType?.AcceptVisitor(this); } Space(); WritePrivateImplementationType(operatorDeclaration.PrivateImplementationType); @@ -2583,7 +2583,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor || operatorDeclaration.OperatorType == OperatorType.CheckedExplicit || operatorDeclaration.OperatorType == OperatorType.Implicit) { - operatorDeclaration.ReturnType.AcceptVisitor(this); + operatorDeclaration.ReturnType?.AcceptVisitor(this); } else { @@ -2658,7 +2658,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor StartNode(propertyDeclaration); WriteAttributes(propertyDeclaration.Attributes); WriteModifiers(propertyDeclaration.Modifiers); - propertyDeclaration.ReturnType.AcceptVisitor(this); + propertyDeclaration.ReturnType?.AcceptVisitor(this); Space(); WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); WriteIdentifier(propertyDeclaration.NameToken); @@ -2682,7 +2682,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } } CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine); - if (!propertyDeclaration.Initializer.IsNull) + if (propertyDeclaration.Initializer is not null) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); @@ -2752,7 +2752,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitSimpleType(SimpleType 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); EndNode(simpleType); } @@ -2956,10 +2958,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteIdentifier(identifier); } - void IAstVisitor.VisitNullNode(AstNode nullNode) - { - } - void IAstVisitor.VisitErrorNode(AstNode errorNode) { StartNode(errorNode); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs index 3f503da3e..2f8997284 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs @@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public override void WriteIdentifier(Identifier identifier) { AssignPendingStartLocations(); - if (!identifier.IsNull) + if (identifier is not null) identifier.SetStartLocation(locationProvider.Location); currentList.Add(identifier); base.WriteIdentifier(identifier); diff --git a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs index 763ddc2a8..1a1f0a226 100644 --- a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.CSharp void VisitAsSequencePoint(AstNode node) { - if (node is null || node.IsNull) + if (node is null) return; StartSequencePoint(node); node.AcceptVisitor(this); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs index aa49d2028..fe9f51496 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs @@ -57,12 +57,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits; - public virtual bool IsNull { - get { - return false; - } - } - TextLocation startLocation = 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. internal void SetChildNode(ref T? field, T? value, Role role) where T : AstNode { - T? newValue = (value == null || value.IsNull) ? null : value; + T? newValue = value; if (field == newValue) return; if (newValue != null) @@ -520,14 +514,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { if (role == null) throw new ArgumentNullException(nameof(role)); - if (child == null || child.IsNull) + if (child == null) return; AddChildUnsafe(child, role); } public void AddChildWithExistingRole(AstNode? child) { - if (child == null || child.IsNull) + if (child == null) return; AddChildUnsafe(child, child.Role); } @@ -555,7 +549,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { if (GetChildSlot(i) == role) { - SetChild(i, child == null || child.IsNull ? null : child); + SetChild(i, child); return; } } @@ -566,11 +560,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { if (role == null) throw new ArgumentNullException(nameof(role)); - if (child == null || child.IsNull) + if (child == null) return; AstNodeCollection? collection = GetCollectionByRole(role); if (collection != null) - collection.InsertNodeBefore((nextSibling == null || nextSibling.IsNull) ? null : nextSibling, child); + collection.InsertNodeBefore(nextSibling, child); else SetChildByRoleUntyped(role, child); } @@ -588,11 +582,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { if (role == null) throw new ArgumentNullException(nameof(role)); - if (child == null || child.IsNull) + if (child == null) return; AstNodeCollection? collection = GetCollectionByRole(role); if (collection != null) - collection.InsertNodeAfter((prevSibling == null || prevSibling.IsNull) ? null : prevSibling, child); + collection.InsertNodeAfter(prevSibling, child); else SetChildByRoleUntyped(role, child); } @@ -618,7 +612,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public void ReplaceWith(AstNode? newNode) { - if (newNode == null || newNode.IsNull) + if (newNode == null) { Remove(); return; @@ -627,7 +621,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return; // nothing to do... 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(); Role role = parent.GetChildSlot(childIndex); @@ -660,7 +654,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax throw new ArgumentNullException(nameof(replaceFunction)); 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? oldSuccessor = NextSibling; @@ -669,7 +663,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax AstNode? replacement = replaceFunction(this); if (oldSuccessor != null && oldSuccessor.parent != oldParent) throw new InvalidOperationException("replace function changed nextSibling of node being replaced?"); - if (!(replacement == null || replacement.IsNull)) + if (replacement != null) { if (replacement.parent != null) throw new InvalidOperationException("replace function must return the root of a tree"); @@ -839,8 +833,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public virtual string ToString(CSharpFormattingOptions? formattingOptions) { - if (IsNull) - return ""; var w = new StringWriter(); AcceptVisitor(new CSharpOutputVisitor(w, formattingOptions ?? FormattingOptionsFactory.CreateMono())); return w.ToString(); @@ -897,15 +889,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override void AddAnnotation(object annotation) { - if (this.IsNull) - throw new InvalidOperationException("Cannot add annotations to the null node"); base.AddAnnotation(annotation); } internal string DebugToString() { - if (IsNull) - return "Null"; string text = ToString(); text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " "); if (text.Length > 100) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs index cb92d87db..fbf95a613 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public void Add(T element) { - if (element == null || element.IsNull) + if (element == null) return; ValidateNewChild(element); list.Add(element); @@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax void Insert(int index, T newItem) { - if (newItem == null || newItem.IsNull) + if (newItem == null) return; ValidateNewChild(newItem); list.Insert(index, newItem); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs index 57e284d25..a455ab48b 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs +++ b/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) { VisitChildren(syntaxTree); @@ -726,15 +718,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax 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) { return VisitChildren(unit); @@ -1410,15 +1393,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax 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) { return VisitChildren(unit, data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs index c9d14f7e9..e0efe131a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs @@ -34,8 +34,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class DocumentationReference : AstNode { - public static readonly Role DeclaringTypeRole = new Role("DeclaringType", AstType.Null); - public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", AstType.Null); + public static readonly Role DeclaringTypeRole = new Role("DeclaringType", null); + public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", null); SymbolKind symbolKind; OperatorType operatorType; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs index 4c206a4c4..a3264906d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs +++ b/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 Role AdditionalArraySpecifierRole = new Role("AdditionalArraySpecifier", null); - public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); + public readonly static Role InitializerRole = new Role("Initializer", null); [Slot("Roles.Type")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [Slot("Roles.Argument")] public partial AstNodeCollection Arguments { get; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs index 625bdb282..5851aee3b 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs @@ -27,6 +27,8 @@ using System; using System.Linq.Expressions; +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -60,14 +62,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static TokenRole RangeRole = new TokenRole(".."); public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole; - public readonly static Role LeftRole = new Role("Left", Expression.Null); - public readonly static Role RightRole = new Role("Right", Expression.Null); + public readonly static Role LeftRole = new Role("Left", null); + public readonly static Role RightRole = new Role("Right", null); public BinaryOperatorExpression() { } - public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right) + public BinaryOperatorExpression(Expression? left, BinaryOperatorType op, Expression? right) { this.Left = left; this.Operator = op; @@ -80,10 +82,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } [Slot(nameof(LeftRole))] - public partial Expression Left { get; set; } + public partial Expression? Left { get; set; } [Slot(nameof(RightRole))] - public partial Expression Right { get; set; } + public partial Expression? Right { get; set; } public static TokenRole GetOperatorRole(BinaryOperatorType op) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs index 129795340..54aa91ecb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs @@ -34,9 +34,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public readonly static Role ConditionRole = Roles.Condition; public readonly static TokenRole QuestionMarkRole = new TokenRole("?"); - public readonly static Role TrueRole = new Role("True", Expression.Null); + public readonly static Role TrueRole = new Role("True", null); public readonly static TokenRole ColonRole = Roles.Colon; - public readonly static Role FalseRole = new Role("False", Expression.Null); + public readonly static Role FalseRole = new Role("False", null); [Slot("ConditionRole")] public partial Expression Condition { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs index dd826fc3d..e2cc9c75c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: true)] public abstract partial class InterpolatedStringContent : AstNode { - public new static readonly Role Role = new Role("InterpolatedStringContent", Syntax.InterpolatedStringContent.Null); + public new static readonly Role Role = new Role("InterpolatedStringContent", null); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs index 7f4b2a7b9..c66f501e0 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public static readonly Role AttributeRole = new Role("Attribute", null); public readonly static TokenRole AsyncModifierRole = new TokenRole("async"); - public static readonly Role BodyRole = new Role("Body", AstNode.Null); + public static readonly Role BodyRole = new Role("Body", null); bool isAsync; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/QueryExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/QueryExpression.cs index a79c10f84..7880b5833 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/QueryExpression.cs +++ b/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 // DEALINGS IN THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -43,7 +45,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class QueryContinuationClause : QueryClause { - public static readonly Role PrecedingQueryRole = new Role("PrecedingQuery", QueryExpression.Null); + public static readonly Role PrecedingQueryRole = new Role("PrecedingQuery", null); public static readonly TokenRole IntoKeywordRole = new TokenRole("into"); [Slot("PrecedingQueryRole")] @@ -63,7 +65,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole InKeywordRole = new TokenRole("in"); [Slot("Roles.Type")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [NameSlot("Roles.Identifier")] 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 Role InExpressionRole = Roles.Expression; public static readonly TokenRole OnKeywordRole = new TokenRole("on"); - public static readonly Role OnExpressionRole = new Role("OnExpression", Expression.Null); + public static readonly Role OnExpressionRole = new Role("OnExpression", null); public static readonly TokenRole EqualsKeywordRole = new TokenRole("equals"); - public static readonly Role EqualsExpressionRole = new Role("EqualsExpression", Expression.Null); + public static readonly Role EqualsExpressionRole = new Role("EqualsExpression", null); public static readonly TokenRole IntoKeywordRole = new TokenRole("into"); - public static readonly Role IntoIdentifierRole = new Role("IntoIdentifier", Identifier.Null); + public static readonly Role IntoIdentifierRole = new Role("IntoIdentifier", null); // Derived from IntoIdentifier (which DoMatch already compares); exclude it to avoid a redundant compare. [ExcludeFromMatch] @@ -131,7 +133,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } [Slot("TypeRole")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [NameSlot("JoinIdentifierRole")] public partial string JoinIdentifier { get; set; } @@ -145,7 +147,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [Slot("EqualsExpressionRole")] public partial Expression EqualsExpression { get; set; } - [NameSlot("IntoIdentifierRole")] + [NameSlot("IntoIdentifierRole", nullOnEmpty: true)] public partial string IntoIdentifier { get; set; } } @@ -206,9 +208,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class QueryGroupClause : QueryClause { public static readonly TokenRole GroupKeywordRole = new TokenRole("group"); - public static readonly Role ProjectionRole = new Role("Projection", Expression.Null); + public static readonly Role ProjectionRole = new Role("Projection", null); public static readonly TokenRole ByKeywordRole = new TokenRole("by"); - public static readonly Role KeyRole = new Role("Key", Expression.Null); + public static readonly Role KeyRole = new Role("Key", null); [Slot("ProjectionRole")] public partial Expression Projection { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs index 3f6c3c9ab..0f9772001 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class RecursivePatternExpression : Expression { - public static readonly Role SubPatternRole = new Role("SubPattern", Syntax.Expression.Null); + public static readonly Role SubPatternRole = new Role("SubPattern", null); [Slot("Roles.Type")] public partial AstType? Type { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs index fca192b16..21273412e 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class StackAllocExpression : Expression { public readonly static TokenRole StackallocKeywordRole = new TokenRole("stackalloc"); - public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); + public readonly static Role InitializerRole = new Role("Initializer", null); [Slot("Roles.Type")] public partial AstType? Type { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs index 0e84510fd..dcdda02a8 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs @@ -41,8 +41,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class SwitchExpressionSection : AstNode { - public static readonly Role PatternRole = new Role("Pattern", Expression.Null); - public static readonly Role BodyRole = new Role("Body", Expression.Null); + public static readonly Role PatternRole = new Role("Pattern", null); + public static readonly Role BodyRole = new Role("Body", null); [Slot("PatternRole")] public partial Expression Pattern { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs index b8eb9f156..87a542278 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class FunctionPointerAstType : AstType { public static readonly TokenRole PointerRole = new TokenRole("*"); - public static readonly Role CallingConventionRole = new Role("CallConv", AstType.Null); + public static readonly Role CallingConventionRole = new Role("CallConv", null); public bool HasUnmanagedCallingConvention { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs index 6bb1d1ddc..c1adf3650 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs @@ -49,8 +49,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override string ToString(CSharpFormattingOptions formattingOptions) { - if (IsNull) - return "Null"; return base.ToString(formattingOptions); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs index 20ae7a33a..999338c42 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public string AttributeTarget { get { - return GetChildByRole(Roles.Identifier).Name; + return GetChildByRole(Roles.Identifier)?.Name ?? string.Empty; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs index efe7100ad..d250f8565 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs @@ -40,7 +40,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class NamespaceDeclaration : AstNode { public static readonly Role MemberRole = SyntaxTree.MemberRole; - public static readonly Role NamespaceNameRole = new Role("NamespaceName", AstType.Null); + public static readonly Role NamespaceNameRole = new Role("NamespaceName", null); public bool IsFileScoped { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs index 772206fb8..0fd0bc03e 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class UsingAliasDeclaration : AstNode { public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); - public static readonly Role AliasRole = new Role("Alias", Identifier.Null); + public static readonly Role AliasRole = new Role("Alias", null); public static readonly Role ImportRole = UsingDeclaration.ImportRole; [NameSlot("AliasRole")] diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs index 1c45901bc..3f7eeb27a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class UsingDeclaration : AstNode { public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); - public static readonly Role ImportRole = new Role("Import", AstType.Null); + public static readonly Role ImportRole = new Role("Import", null); [Slot("ImportRole")] public partial AstType Import { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs index b01f791e9..b7b9422af 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs @@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static Identifier Create(string name, TextLocation location) { if (string.IsNullOrEmpty(name)) - return Identifier.Null; + return new Identifier(string.Empty, location); if (name[0] == '@') return new Identifier(name.Substring(1), new TextLocation(location.Line, location.Column + 1)) { IsVerbatim = true }; else @@ -116,7 +116,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static Identifier Create(string name, TextLocation location, bool isVerbatim) { if (string.IsNullOrEmpty(name)) - return Identifier.Null; + return new Identifier(string.Empty, location); if (isVerbatim) return new Identifier(name, location) { IsVerbatim = true }; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/MemberType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/MemberType.cs index 1ab2c4c6b..09b3d080a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/MemberType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/MemberType.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class MemberType : AstType { - public static readonly Role TargetRole = new Role("Target", AstType.Null); + public static readonly Role TargetRole = new Role("Target", null); bool isDoubleColon; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs index 07d8d3bba..85993d6d4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs +++ b/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) { match.Add(this.groupName, other); - return other != null && !other.IsNull; + return other != null; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs index b99d5e26b..acdb9b425 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs @@ -26,8 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching /// public interface INode { - bool IsNull { get; } - bool DoMatch(INode other, Match match); /// diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs index 0f7ce04fb..8da9f0924 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs +++ b/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) { - if (other == null || other.IsNull) + if (other == null) return true; else return childNode.DoMatch(other, match); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs index 2f670eb5d..a73e6cc25 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs +++ b/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 virtual bool DoMatchCollection(IReadOnlyList other, int pos, Match match, BacktrackingInfo backtrackingInfo) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs index 17c57c606..a0612f18c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs +++ b/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) { - if (other == null || other.IsNull) + if (other == null) return this.MinCount <= 0; else return this.MaxCount >= 1 && childNode.DoMatch(other, match); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs index df91e1285..3dc9acd32 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs @@ -31,22 +31,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly Role Root = AstNode.RootRole; // some pre defined constants for common roles - public static readonly Role Identifier = new Role("Identifier", Syntax.Identifier.Null); - public static readonly Role Body = new Role("Body", BlockStatement.Null); + public static readonly Role Identifier = new Role("Identifier", null); + public static readonly Role Body = new Role("Body", null); public static readonly Role Parameter = new Role("Parameter", null); - public static readonly Role Argument = new Role("Argument", Syntax.Expression.Null); - public static readonly Role Type = new Role("Type", AstType.Null); - public static readonly Role Expression = new Role("Expression", Syntax.Expression.Null); - public static readonly Role TargetExpression = new Role("Target", Syntax.Expression.Null); - public readonly static Role Condition = new Role("Condition", Syntax.Expression.Null); + public static readonly Role Argument = new Role("Argument", null); + public static readonly Role Type = new Role("Type", null); + public static readonly Role Expression = new Role("Expression", null); + public static readonly Role TargetExpression = new Role("Target", null); + public readonly static Role Condition = new Role("Condition", null); public static readonly Role TypeParameter = new Role("TypeParameter", null); - public static readonly Role TypeArgument = new Role("TypeArgument", AstType.Null); + public static readonly Role TypeArgument = new Role("TypeArgument", null); public readonly static Role Constraint = new Role("Constraint", null); - public static readonly Role Variable = new Role("Variable", VariableInitializer.Null); - public static readonly Role EmbeddedStatement = new Role("EmbeddedStatement", Statement.Null); + public static readonly Role Variable = new Role("Variable", null); + public static readonly Role EmbeddedStatement = new Role("EmbeddedStatement", null); public readonly static Role TypeMemberRole = new Role("TypeMember", null); - public static readonly Role VariableDesignationRole = new Role("VariableDesignation", VariableDesignation.Null); + public static readonly Role VariableDesignationRole = new Role("VariableDesignation", null); // some pre defined constants for most used punctuation public static readonly TokenRole LPar = new TokenRole("("); @@ -67,12 +67,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly Role Comment = new Role("Comment", null); public static readonly Role PreProcessorDirective = new Role("PreProcessorDirective", null); - public readonly static Role BaseType = new Role("BaseType", AstType.Null); + public readonly static Role BaseType = new Role("BaseType", null); public static readonly Role Attribute = new Role("Attribute", null); public readonly static TokenRole WhereKeyword = new TokenRole("where"); - public readonly static Role ConstraintTypeParameter = new Role("TypeParameter", SimpleType.Null); + public readonly static Role ConstraintTypeParameter = new Role("TypeParameter", null); public readonly static TokenRole DelegateKeyword = new TokenRole("delegate"); public static readonly TokenRole ExternKeyword = new TokenRole("extern"); public static readonly TokenRole AliasKeyword = new TokenRole("alias"); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SimpleType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SimpleType.cs index fd6e9ffb9..be57b9d51 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SimpleType.cs +++ b/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; } [Slot("Roles.TypeArgument")] diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs index ece4c2bee..9d4267497 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: true, hasPatternPlaceholder: true)] public partial class BlockStatement : Statement, IEnumerable { - public static readonly Role StatementRole = new Role("Statement", Statement.Null); + public static readonly Role StatementRole = new Role("Statement", null); [Slot("StatementRole")] public partial AstNodeCollection Statements { get; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs index acab3e493..dc19b9241 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs @@ -35,8 +35,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class ForStatement : Statement { public static readonly TokenRole ForKeywordRole = new TokenRole("for"); - public readonly static Role InitializerRole = new Role("Initializer", Statement.Null); - public readonly static Role IteratorRole = new Role("Iterator", Statement.Null); + public readonly static Role InitializerRole = new Role("Initializer", null); + public readonly static Role IteratorRole = new Role("Iterator", null); /// /// Gets the list of initializer statements. diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs index a240e6288..544976450 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs +++ b/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 Role ConditionRole = Roles.Condition; - public readonly static Role TrueRole = new Role("True", Statement.Null); + public readonly static Role TrueRole = new Role("True", null); public readonly static TokenRole ElseKeywordRole = new TokenRole("else"); - public readonly static Role FalseRole = new Role("False", Statement.Null); + public readonly static Role FalseRole = new Role("False", null); [Slot("ConditionRole")] public partial Expression Condition { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs index 8ddccf636..9e3a2ac94 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs @@ -35,10 +35,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class TryCatchStatement : Statement { public static readonly TokenRole TryKeywordRole = new TokenRole("try"); - public static readonly Role TryBlockRole = new Role("TryBlock", BlockStatement.Null); - public static readonly Role CatchClauseRole = new Role("CatchClause", CatchClause.Null); + public static readonly Role TryBlockRole = new Role("TryBlock", null); + public static readonly Role CatchClauseRole = new Role("CatchClause", null); public static readonly TokenRole FinallyKeywordRole = new TokenRole("finally"); - public static readonly Role FinallyBlockRole = new Role("FinallyBlock", BlockStatement.Null); + public static readonly Role FinallyBlockRole = new Role("FinallyBlock", null); [Slot("TryBlockRole")] public partial BlockStatement TryBlock { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs index 9b373bfae..ce92200fc 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs +++ b/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 AwaitRole = UnaryOperatorExpression.AwaitRole; - public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", AstNode.Null); + public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", null); public bool IsAsync { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs index 9fd2e4c22..da0a3cad4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class SyntaxTree : AstNode { - public static readonly Role MemberRole = new Role("Member", AstNode.Null); + public static readonly Role MemberRole = new Role("Member", null); [Slot("MemberRole")] public partial AstNodeCollection Members { get; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs index d47a84457..5f346c6a6 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class TupleAstType : AstType { - public static readonly Role ElementRole = new Role("Element", TupleTypeElement.Null); + public static readonly Role ElementRole = new Role("Element", null); [Slot("ElementRole")] public partial AstNodeCollection Elements { get; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs index 03df28adc..7fedb0d6c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs @@ -63,7 +63,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } public override Identifier NameToken { - get { return Identifier.Null; } + get { return null!; } set { } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs index f669f797a..73f13c345 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class ConstructorDeclaration : EntityDeclaration { - public static readonly Role InitializerRole = new Role("Initializer", ConstructorInitializer.Null); + public static readonly Role InitializerRole = new Role("Initializer", null); public override SymbolKind SymbolKind { get { return SymbolKind.Constructor; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs index 931e2d7da..b4bec4b16 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs +++ b/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); // 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 Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); + public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", null); public abstract SymbolKind SymbolKind { get; } @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public virtual string Name { get { - return GetChildByRole(Roles.Identifier).Name; + return GetChildByRole(Roles.Identifier)?.Name ?? string.Empty; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty)); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs index 294db8533..9cc55205d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [DecompilerAstNode(hasNullNode: false)] public partial class EnumMemberDeclaration : EntityDeclaration { - public static readonly Role InitializerRole = new Role("Initializer", Expression.Null); + public static readonly Role InitializerRole = new Role("Initializer", null); public override SymbolKind SymbolKind { get { return SymbolKind.Field; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs index 661b22e16..669897752 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { - get { return Identifier.Null; } + get { return null!; } 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 RemoveKeywordRole = new TokenRole("remove"); - public static readonly Role AddAccessorRole = new Role("AddAccessor", Accessor.Null); - public static readonly Role RemoveAccessorRole = new Role("RemoveAccessor", Accessor.Null); + public static readonly Role AddAccessorRole = new Role("AddAccessor", null); + public static readonly Role RemoveAccessorRole = new Role("RemoveAccessor", null); public override SymbolKind SymbolKind { get { return SymbolKind.Event; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs index b7758d7ed..6e2418695 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs @@ -49,20 +49,5 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public ExtensionDeclaration() { } - - public override void AcceptVisitor(IAstVisitor visitor) - { - visitor.VisitExtensionDeclaration(this); - } - - public override T AcceptVisitor(IAstVisitor visitor) - { - return visitor.VisitExtensionDeclaration(this); - } - - public override S AcceptVisitor(IAstVisitor visitor, T data) - { - return visitor.VisitExtensionDeclaration(this, data); - } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs index 92f9d3545..94d23309f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { - get { return Identifier.Null; } + get { return null!; } set { throw new NotSupportedException(); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs index 539ebe6c8..ce8031db2 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs +++ b/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 Role GetterRole = PropertyDeclaration.GetterRole; public static readonly Role SetterRole = PropertyDeclaration.SetterRole; - public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", Expression.Null); + public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", null); public override SymbolKind SymbolKind { get { return SymbolKind.Indexer; } @@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { - get { return Identifier.Null; } + get { return null!; } set { throw new NotSupportedException(); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs index 7e233c3eb..09e940583 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs @@ -324,7 +324,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { - get { return Identifier.Null; } + get { return null!; } set { throw new NotSupportedException(); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs index 6b4ba5b98..667334828 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [Slot("Roles.Type")] public partial AstType? Type { get; set; } - [NameSlot("Roles.Identifier")] + [NameSlot("Roles.Identifier", nullOnEmpty: true)] public partial string Name { get; set; } [Slot("Roles.Expression")] diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs index b738e50e6..dda4e2fdc 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs +++ b/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 SetKeywordRole = new TokenRole("set"); public static readonly TokenRole InitKeywordRole = new TokenRole("init"); - public static readonly Role GetterRole = new Role("Getter", Accessor.Null); - public static readonly Role SetterRole = new Role("Setter", Accessor.Null); - public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", Expression.Null); + public static readonly Role GetterRole = new Role("Getter", null); + public static readonly Role SetterRole = new Role("Setter", null); + public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", null); public override SymbolKind SymbolKind { get { return SymbolKind.Property; } @@ -75,7 +75,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial Accessor? Setter { get; set; } [Slot("Roles.Expression")] - public partial Expression Initializer { get; set; } + public partial Expression? Initializer { get; set; } [Slot("ExpressionBodyRole")] public partial Expression? ExpressionBody { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 827188068..dbcb8ad86 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -2081,14 +2081,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else { - return BlockStatement.Null; + return null; } } Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute) { if (accessor == null) - return Accessor.Null; + return null; Accessor decl = new Accessor(); if (ShowAttributes) { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs index ebf2e3fc3..96f6b9ce6 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs @@ -393,7 +393,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { // We cannot use checked/unchecked for top-level-expressions. } - else if (expr.Role.IsValid(Expression.Null)) + else if (expr.Role is Role || expr.Role is Role) { // We use '<' so that expressions are introduced on the deepest level possible (goal 3) var costIfWrapWithChecked = result.CostInCheckedContext.WrapInCheckedExpr(); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs index 88b793763..11d2de87b 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { var memberNames = typeDecl.Members.Select(m => { var type = m.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); - return type.IsNull ? m.Name : type + "." + m.Name; + return type is null ? m.Name : type + "." + m.Name; }).ToHashSet(); // memberNames does not include fields or non-custom events because those // don't have a single name, but a list of VariableInitializers. diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index badf5395a..d115ed1c7 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -258,6 +258,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms // lambda, so this match always succeeds. bool matched = MatchSimpleLambda(lambda, out parameter, out orderExpression); Debug.Assert(matched); + Debug.Assert(orderExpression != null); } // The chain was validated and every iteration re-matched a simple lambda, // so the final range-variable parameter is set. diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs index 4785f4019..6450e412a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs @@ -97,8 +97,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { // transform "*(ptr + int)" to "ptr[int]" IndexerExpression indexer = new IndexerExpression(); - indexer.Target = bop.Left.Detach(); - indexer.Arguments.Add(bop.Right.Detach()); + indexer.Target = bop.Left!.Detach(); + indexer.Arguments.Add(bop.Right!.Detach()); indexer.CopyAnnotationsFrom(unaryOperatorExpression); indexer.CopyAnnotationsFrom(bop); unaryOperatorExpression.ReplaceWith(indexer); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 9bd7084bd..ff24d8ec9 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -93,7 +93,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms void DoTransform(Statement? statement, Statement parent) { - if (statement is null || statement.IsNull) + if (statement is null) return; if (context.Settings.AlwaysUseBraces) { @@ -122,8 +122,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms static void InsertBlock(Statement statement) { - if (statement.IsNull) - return; if (!(statement is BlockStatement)) { var b = new BlockStatement(); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs index d994f5e0f..3b1ad8a20 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs +++ b/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 (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left) - && IsImplicitlyConvertible(binary.Right, expectedType)) + && binary.Right != null && IsImplicitlyConvertible(binary.Right, expectedType)) { assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator); if (assignment.Operator != AssignmentOperatorType.Assign) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index 16221ba47..1cf5192bc 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -546,7 +546,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms break; case PropertyDeclaration pd: Debug.Assert(pd.IsAutomaticProperty); - if (pd.Initializer.IsNull) + if (pd.Initializer is null) { pd.Initializer = initializer.Detach(); } diff --git a/ICSharpCode.Decompiler/IL/ILAmbience.cs b/ICSharpCode.Decompiler/IL/ILAmbience.cs index c750ff722..4307c77b4 100644 --- a/ICSharpCode.Decompiler/IL/ILAmbience.cs +++ b/ICSharpCode.Decompiler/IL/ILAmbience.cs @@ -400,7 +400,7 @@ namespace ICSharpCode.Decompiler.IL builder.Append(type.CallingConvention.ToILSyntax()); builder.Append(' '); } - type.ReturnType.AcceptVisitor(this); + type.ReturnType?.AcceptVisitor(this); builder.Append(" *("); bool first = true; foreach (var p in type.ParameterTypes) diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 787adb1b8..f8cd2e1ef 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/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. var member = GetCurrentMemberReference(); var node = nodeStack.Peek(); - if (member != null && node.GetChildByRole(Roles.Identifier).IsNull) + if (member != null && node.GetChildByRole(Roles.Identifier) is null) { switch (member) { diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 2dc2901bc..e31fadc04 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -627,7 +627,7 @@ namespace ICSharpCode.ILSpy.Languages } break; case PropertyDeclaration pd: - if (pd.Initializer.IsNull) + if (pd.Initializer is null) { pd.Remove(); removedSymbols.Add(pd.GetSymbol());