From 1f4fd68b3f7e24bb5b6ebc91f4fa27450b912e5b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 16 Jun 2026 14:36:29 +0200 Subject: [PATCH] Make optional AST slots nullable Optional single-child slots return T? with a real null instead of a role null-object, taking the C# grammar as the oracle for which slots are optional. The generator emits the property type as T? and matches it with MatchOptional, and consumers move from .IsNull to is null / ?.. This covers the optional statement, member, try-catch, creation-initializer and pattern slots and the optional NameSlot tokens. A few slots the grammar marks required but the decompiler legitimately leaves empty (the implicit-element-access target, an implicitly-typed lambda parameter's type) are flipped to nullable as well. Assisted-by: Claude:claude-opus-4-8:Claude Code --- .../DecompilerSyntaxTreeGenerator.cs | 15 +++- .../CSharp/CSharpDecompiler.cs | 2 +- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 7 +- .../CSharp/ExpressionBuilder.cs | 6 +- .../CSharp/OutputVisitor/CSharpAmbience.cs | 2 +- .../OutputVisitor/CSharpOutputVisitor.cs | 78 +++++++++---------- .../OutputVisitor/InsertParenthesesVisitor.cs | 26 ++++--- .../CSharp/SequencePointBuilder.cs | 12 +-- .../CSharp/StatementBuilder.cs | 4 +- .../CSharp/Syntax/DocumentationReference.cs | 8 +- .../Expressions/ArrayCreateExpression.cs | 4 +- .../Syntax/Expressions/IndexerExpression.cs | 10 ++- .../Expressions/ObjectCreateExpression.cs | 6 +- .../Expressions/RecursivePatternExpression.cs | 6 +- .../Expressions/StackAllocExpression.cs | 10 ++- .../CSharp/Syntax/Statements/ForStatement.cs | 4 +- .../Syntax/Statements/IfElseStatement.cs | 6 +- .../Syntax/Statements/ReturnStatement.cs | 4 +- .../Syntax/Statements/SwitchStatement.cs | 4 +- .../Syntax/Statements/ThrowStatement.cs | 4 +- .../Syntax/Statements/TryCatchStatement.cs | 8 +- .../CSharp/Syntax/TupleAstType.cs | 2 +- .../CSharp/Syntax/TypeMembers/Accessor.cs | 2 +- .../TypeMembers/ConstructorDeclaration.cs | 6 +- .../TypeMembers/DestructorDeclaration.cs | 4 +- .../TypeMembers/EnumMemberDeclaration.cs | 4 +- .../Syntax/TypeMembers/EventDeclaration.cs | 8 +- .../Syntax/TypeMembers/IndexerDeclaration.cs | 10 ++- .../Syntax/TypeMembers/MethodDeclaration.cs | 6 +- .../Syntax/TypeMembers/OperatorDeclaration.cs | 10 ++- .../TypeMembers/ParameterDeclaration.cs | 4 +- .../Syntax/TypeMembers/PropertyDeclaration.cs | 14 ++-- .../Syntax/TypeMembers/VariableInitializer.cs | 6 +- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 4 +- .../Transforms/NormalizeBlockStatements.cs | 24 +++--- .../Transforms/PatternStatementTransform.cs | 36 +++++---- .../CSharp/Transforms/PrettifyAssignments.cs | 2 +- .../ReplaceMethodCallsWithOperators.cs | 4 +- ...ransformFieldAndConstructorInitializers.cs | 22 +++--- .../DebugInfo/DebugInfoGenerator.cs | 4 +- ILSpy/Languages/CSharpLanguage.cs | 6 +- 41 files changed, 240 insertions(+), 164 deletions(-) diff --git a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs index 6f56641ac..1866ebd28 100644 --- a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs +++ b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs @@ -143,7 +143,8 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator string roleExpr = (string)nameSlotAttr.ConstructorArguments[0].Value!; bool nullOnEmpty = nameSlotAttr.ConstructorArguments.Length > 1 && (bool)nameSlotAttr.ConstructorArguments[1].Value!; string tokenName = property.Name + "Token"; - slots.Add((roleExpr, false, tokenName, "Identifier", "Identifier", false, false, SlotKindName(roleExpr), false)); + // An optional name (nullOnEmpty) makes the backing token a real nullable slot: an absent name is a null token. + slots.Add((roleExpr, false, tokenName, "Identifier", "Identifier", false, nullOnEmpty, SlotKindName(roleExpr), false)); nameSlots.Add((property.Name, tokenName, nullOnEmpty)); } } @@ -363,7 +364,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator else { builder.AppendLine($"\t{type}? {field};"); - builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}{type} {name}"); + builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}{type}{(isNullable ? "?" : "")} {name}"); builder.AppendLine("\t{"); builder.AppendLine($"\t\tget => {field}{(isNullable ? "" : $" ?? {roleExpr}.NullObject")};"); builder.AppendLine($"\t\tset => SetChildNode(ref {field}, value, {roleExpr});"); @@ -379,11 +380,17 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator { builder.AppendLine($"\tpublic partial string {stringName}"); builder.AppendLine("\t{"); - builder.AppendLine($"\t\tget => {tokenName}.Name;"); if (nullOnEmpty) - builder.AppendLine($"\t\tset => {tokenName} = string.IsNullOrEmpty(value) ? null! : global::ICSharpCode.Decompiler.CSharp.Syntax.Identifier.Create(value);"); + { + // The token is a nullable slot, so guard the read and clear it (to null) on an empty name. + builder.AppendLine($"\t\tget => {tokenName}?.Name ?? string.Empty;"); + builder.AppendLine($"\t\tset => {tokenName} = string.IsNullOrEmpty(value) ? null : global::ICSharpCode.Decompiler.CSharp.Syntax.Identifier.Create(value);"); + } else + { + builder.AppendLine($"\t\tget => {tokenName}.Name;"); builder.AppendLine($"\t\tset => {tokenName} = global::ICSharpCode.Decompiler.CSharp.Syntax.Identifier.Create(value);"); + } builder.AppendLine("\t}"); builder.AppendLine(); } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 9353f8869..d9ef64ae0 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1663,7 +1663,7 @@ namespace ICSharpCode.Decompiler.CSharp typeDecl.Members.AddRange(entityMap[member]); } - if (typeDecl.Members.OfType().Any(idx => idx.PrivateImplementationType.IsNull)) + if (typeDecl.Members.OfType().Any(idx => idx.PrivateImplementationType is null)) { // Remove the [DefaultMember] attribute if the class contains indexers RemoveAttribute(typeDecl, KnownAttribute.DefaultMember); diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 837d6faec..33bc27aba 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -731,7 +731,7 @@ namespace ICSharpCode.Decompiler.CSharp var assignment = HandleAccessorCall(expectedTargetDetails, method, unused, argumentList.Arguments.ToList(), argumentList.ArgumentNames); - if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull) + if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && indexer.Target is not null) indexer.Target.Remove(); if (value != null) @@ -1093,7 +1093,7 @@ namespace ICSharpCode.Decompiler.CSharp return false; case ArrayCreateResolveResult { Type: ArrayType { ElementType: var type3 }, SizeArguments: [{ ConstantValue: int arrayLength }] }: elementType = type3; - arguments = new(((ArrayCreateExpression)paramsArgument.Expression).Initializer.Elements.Select(e => new TranslatedExpression(e))); + arguments = new(((ArrayCreateExpression)paramsArgument.Expression).Initializer?.Elements.Select(e => new TranslatedExpression(e)) ?? []); parameters = new List(arrayLength); for (int i = 0; i < arrayLength; i++) { @@ -1423,7 +1423,8 @@ namespace ICSharpCode.Decompiler.CSharp continue; if (child is ReturnStatement ret) { - ret.Expression = new TranslatedExpression(ret.Expression.Detach()).ConvertTo(returnType, expressionBuilder); + if (ret.Expression is not null) + ret.Expression = new TranslatedExpression(ret.Expression.Detach()).ConvertTo(returnType, expressionBuilder); continue; } ModifyReturnStatementInsideLambda(returnType, child); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 46e79abdc..9680c50f0 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2469,7 +2469,7 @@ namespace ICSharpCode.Decompiler.CSharp } bool isLambda = false; - if (ame.Parameters.Any(p => p.Type.IsNull)) + if (ame.Parameters.Any(p => p.Type is null)) { // if there is an anonymous type involved, we are forced to use a lambda expression. isLambda = true; @@ -2532,7 +2532,7 @@ namespace ICSharpCode.Decompiler.CSharp function, delegateType, inferredReturnType, hasParameterList: isLambda || ame.HasParameterList, isAnonymousMethod: !isLambda, - isImplicitlyTyped: ame.Parameters.Any(p => p.Type.IsNull)); + isImplicitlyTyped: ame.Parameters.Any(p => p.Type is null)); TranslatedExpression translatedLambda = replacement.WithILInstruction(function).WithRR(rr); return new CastExpression(ConvertType(delegateType), translatedLambda) @@ -2558,7 +2558,7 @@ namespace ICSharpCode.Decompiler.CSharp { if (node is ReturnStatement ret) { - if (!ret.Expression.IsNull) + if (ret.Expression is not null) { returnExpressions.Add(ret.Expression.GetResolveResult()); } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 1eb0d8793..d4a93a292 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -162,7 +162,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } if ((ConversionFlags & ConversionFlags.ShowParameterDefaultValues) == 0) { - param.DefaultExpression.Detach(); + param.DefaultExpression?.Detach(); } if (first) { diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index d6619a7cf..9a9579a10 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -244,7 +244,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return false; if (!(kind == SlotKind.Getter || kind == SlotKind.Setter)) return false; - bool isAutoProperty = accessor.Body.IsNull + bool isAutoProperty = accessor.Body is null && !accessor.Attributes.Any() && policy.AutoPropertyFormatting == PropertyFormatting.SingleLine; return isAutoProperty; @@ -531,7 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style, bool newLine = true) { - if (body.IsNull) + if (body is null || body.IsNull) { Semicolon(); } @@ -552,7 +552,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor protected virtual void WritePrivateImplementationType(AstType privateImplementationType) { - if (!privateImplementationType.IsNull) + if (privateImplementationType is not null && !privateImplementationType.IsNull) { privateImplementationType.AcceptVisitor(this); WriteToken(Roles.Dot); @@ -620,7 +620,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { specifier.AcceptVisitor(this); } - arrayCreateExpression.Initializer.AcceptVisitor(this); + arrayCreateExpression.Initializer?.AcceptVisitor(this); EndNode(arrayCreateExpression); } @@ -925,7 +925,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(recursivePatternExpression); - recursivePatternExpression.Type.AcceptVisitor(this); + recursivePatternExpression.Type?.AcceptVisitor(this); Space(); if (recursivePatternExpression.IsPositional) { @@ -946,7 +946,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { WriteToken(Roles.RBrace); } - if (!recursivePatternExpression.Designation.IsNull) + if (recursivePatternExpression.Designation is not null) { Space(); recursivePatternExpression.Designation.AcceptVisitor(this); @@ -979,7 +979,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitIndexerExpression(IndexerExpression indexerExpression) { StartNode(indexerExpression); - indexerExpression.Target.AcceptVisitor(this); + indexerExpression.Target?.AcceptVisitor(this); Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); EndNode(indexerExpression); @@ -1050,7 +1050,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return true; } var p = lambdaExpression.Parameters.Single(); - return !(p.Type.IsNull && p.ParameterModifier == ReferenceKind.None && !p.IsParams); + return !(p.Type is null && p.ParameterModifier == ReferenceKind.None && !p.IsParams); } public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) @@ -1102,13 +1102,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor StartNode(objectCreateExpression); WriteKeyword(ObjectCreateExpression.NewKeywordRole); objectCreateExpression.Type.AcceptVisitor(this); - bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer.IsNull; + bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer is null; if (useParenthesis) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(objectCreateExpression.Arguments, policy.SpaceWithinMethodCallParentheses); } - objectCreateExpression.Initializer.AcceptVisitor(this); + objectCreateExpression.Initializer?.AcceptVisitor(this); EndNode(objectCreateExpression); } @@ -1212,9 +1212,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(stackAllocExpression); WriteKeyword(StackAllocExpression.StackallocKeywordRole); - stackAllocExpression.Type.AcceptVisitor(this); - WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression }); - stackAllocExpression.Initializer.AcceptVisitor(this); + stackAllocExpression.Type?.AcceptVisitor(this); + WriteCommaSeparatedListInBrackets(stackAllocExpression.CountExpression is { } stackAllocCount ? new[] { stackAllocCount } : Array.Empty()); + stackAllocExpression.Initializer?.AcceptVisitor(this); EndNode(stackAllocExpression); } @@ -1831,7 +1831,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.Semicolon); Space(policy.SpaceAfterForSemicolon); - forStatement.Condition.AcceptVisitor(this); + forStatement.Condition?.AcceptVisitor(this); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); if (forStatement.Iterators.Any()) @@ -1886,7 +1886,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(policy.SpacesWithinIfParentheses); RPar(); - if (ifElseStatement.FalseStatement.IsNull) + if (ifElseStatement.FalseStatement is null) { WriteEmbeddedStatement(ifElseStatement.TrueStatement); } @@ -1947,7 +1947,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(returnStatement); WriteKeyword(ReturnStatement.ReturnKeywordRole); - if (!returnStatement.Expression.IsNull) + if (returnStatement.Expression is not null) { Space(); returnStatement.Expression.AcceptVisitor(this); @@ -2024,7 +2024,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitCaseLabel(CaseLabel caseLabel) { StartNode(caseLabel); - if (caseLabel.Expression.IsNull) + if (caseLabel.Expression is null) { WriteKeyword(CaseLabel.DefaultKeywordRole); } @@ -2070,7 +2070,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(throwStatement); WriteKeyword(ThrowStatement.ThrowKeywordRole); - if (!throwStatement.Expression.IsNull) + if (throwStatement.Expression is not null) { Space(); throwStatement.Expression.AcceptVisitor(this); @@ -2092,7 +2092,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); catchClause.AcceptVisitor(this); } - if (!tryCatchStatement.FinallyBlock.IsNull) + if (tryCatchStatement.FinallyBlock is not null) { if (policy.FinallyNewLinePlacement == NewLinePlacement.SameLine) Space(); @@ -2109,7 +2109,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(catchClause); WriteKeyword(CatchClause.CatchKeywordRole); - if (!catchClause.Type.IsNull) + if (catchClause.Type is not null) { Space(policy.SpaceBeforeCatchParentheses); LPar(); @@ -2123,7 +2123,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(policy.SpacesWithinCatchParentheses); RPar(); } - if (!catchClause.Condition.IsNull) + if (catchClause.Condition is not null) { Space(); WriteKeyword(CatchClause.WhenKeywordRole); @@ -2313,7 +2313,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteIdentifier(constructorDeclaration.NameToken); Space(policy.SpaceBeforeConstructorDeclarationParentheses); WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); - if (!constructorDeclaration.Initializer.IsNull) + if (constructorDeclaration.Initializer is not null) { NewLine(); writer.Indent(); @@ -2370,7 +2370,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(enumMemberDeclaration.Attributes); WriteModifiers(enumMemberDeclaration.Modifiers); WriteIdentifier(enumMemberDeclaration.NameToken); - if (!enumMemberDeclaration.Initializer.IsNull) + if (enumMemberDeclaration.Initializer is not null) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); @@ -2500,14 +2500,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); - if (indexerDeclaration.ExpressionBody.IsNull) + if (indexerDeclaration.ExpressionBody is null) { bool isSingleLine = (policy.AutoPropertyFormatting == PropertyFormatting.SingleLine) - && (indexerDeclaration.Getter.IsNull || indexerDeclaration.Getter.Body.IsNull) - && (indexerDeclaration.Setter.IsNull || indexerDeclaration.Setter.Body.IsNull) - && !indexerDeclaration.Getter.Attributes.Any() - && !indexerDeclaration.Setter.Attributes.Any(); + && (indexerDeclaration.Getter is null || indexerDeclaration.Getter.Body is null) + && (indexerDeclaration.Setter is null || indexerDeclaration.Setter.Body is null) + && (indexerDeclaration.Getter is null || !indexerDeclaration.Getter.Attributes.Any()) + && (indexerDeclaration.Setter is null || !indexerDeclaration.Setter.Attributes.Any()); OpenBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, newLine: !isSingleLine); if (isSingleLine) Space(); @@ -2634,8 +2634,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(); break; } - parameterDeclaration.Type.AcceptVisitor(this); - if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) + parameterDeclaration.Type?.AcceptVisitor(this); + if (parameterDeclaration.Type is not null && !string.IsNullOrEmpty(parameterDeclaration.Name)) { Space(); } @@ -2643,7 +2643,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { WriteIdentifier(parameterDeclaration.NameToken); } - if (!parameterDeclaration.DefaultExpression.IsNull) + if (parameterDeclaration.DefaultExpression is not null) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); @@ -2662,14 +2662,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(); WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); WriteIdentifier(propertyDeclaration.NameToken); - if (propertyDeclaration.ExpressionBody.IsNull) + if (propertyDeclaration.ExpressionBody is null) { bool isSingleLine = (policy.AutoPropertyFormatting == PropertyFormatting.SingleLine) - && (propertyDeclaration.Getter.IsNull || propertyDeclaration.Getter.Body.IsNull) - && (propertyDeclaration.Setter.IsNull || propertyDeclaration.Setter.Body.IsNull) - && !propertyDeclaration.Getter.Attributes.Any() - && !propertyDeclaration.Setter.Attributes.Any(); + && (propertyDeclaration.Getter is null || propertyDeclaration.Getter.Body is null) + && (propertyDeclaration.Setter is null || propertyDeclaration.Setter.Body is null) + && (propertyDeclaration.Getter is null || !propertyDeclaration.Getter.Attributes.Any()) + && (propertyDeclaration.Setter is null || !propertyDeclaration.Setter.Attributes.Any()); OpenBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, newLine: !isSingleLine); if (isSingleLine) Space(); @@ -2714,7 +2714,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(variableInitializer); WriteIdentifier(variableInitializer.NameToken); - if (!variableInitializer.Initializer.IsNull) + if (variableInitializer.Initializer is not null) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); @@ -2788,7 +2788,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(tupleTypeElement); tupleTypeElement.Type.AcceptVisitor(this); - if (!tupleTypeElement.NameToken.IsNull) + if (tupleTypeElement.NameToken is not null) { Space(); tupleTypeElement.NameToken.AcceptVisitor(this); @@ -3098,7 +3098,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitDocumentationReference(DocumentationReference documentationReference) { StartNode(documentationReference); - if (!documentationReference.DeclaringType.IsNull) + if (documentationReference.DeclaringType is not null) { documentationReference.DeclaringType.AcceptVisitor(this); if (documentationReference.SymbolKind != SymbolKind.TypeDefinition) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index ace70d800..dfd1132be 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -199,17 +199,21 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public override void VisitIndexerExpression(IndexerExpression indexerExpression) { - ParenthesizeIfRequired(indexerExpression.Target, PrecedenceLevel.Primary); - switch (indexerExpression.Target) + // An implicit-element-access indexer (e.g. the '[i]' in a dictionary initializer) has no target. + if (indexerExpression.Target is not null) { - case ArrayCreateExpression ace when InsertParenthesesForReadability || ace.Initializer.IsNull: - // require parentheses for "(new int[1])[0]" - Parenthesize(indexerExpression.Target); - break; - case StackAllocExpression sae when InsertParenthesesForReadability || sae.Initializer.IsNull: - // require parentheses for "(stackalloc int[1])[0]" - Parenthesize(indexerExpression.Target); - break; + ParenthesizeIfRequired(indexerExpression.Target, PrecedenceLevel.Primary); + switch (indexerExpression.Target) + { + case ArrayCreateExpression ace when InsertParenthesesForReadability || ace.Initializer is null: + // require parentheses for "(new int[1])[0]" + Parenthesize(indexerExpression.Target); + break; + case StackAllocExpression sae when InsertParenthesesForReadability || sae.Initializer is null: + // require parentheses for "(stackalloc int[1])[0]" + Parenthesize(indexerExpression.Target); + break; + } } base.VisitIndexerExpression(indexerExpression); } @@ -487,7 +491,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public override void VisitVariableInitializer(VariableInitializer variableInitializer) { - if (!variableInitializer.Initializer.IsNull) + if (variableInitializer.Initializer is not null) HandleAssignmentRHS(variableInitializer.Initializer); base.VisitVariableInitializer(variableInitializer); } diff --git a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs index 171b177c0..763ddc2a8 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.IsNull) + if (node is null || node.IsNull) return; StartSequencePoint(node); node.AcceptVisitor(this); @@ -165,7 +165,7 @@ namespace ICSharpCode.Decompiler.CSharp public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { - if (!propertyDeclaration.ExpressionBody.IsNull) + if (propertyDeclaration.ExpressionBody is not null) { VisitAsSequencePoint(propertyDeclaration.ExpressionBody); } @@ -177,7 +177,7 @@ namespace ICSharpCode.Decompiler.CSharp public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { - if (!indexerDeclaration.ExpressionBody.IsNull) + if (indexerDeclaration.ExpressionBody is not null) { VisitAsSequencePoint(indexerDeclaration.ExpressionBody); } @@ -385,7 +385,7 @@ namespace ICSharpCode.Decompiler.CSharp public override void VisitCatchClause(CatchClause catchClause) { - if (catchClause.Condition.IsNull) + if (catchClause.Condition is null) { var tryCatchHandler = catchClause.Annotation(); if (tryCatchHandler != null && !tryCatchHandler.ExceptionSpecifierILRange.IsEmpty) @@ -432,9 +432,9 @@ namespace ICSharpCode.Decompiler.CSharp // the 'catch' keyword when there is no exception specifier. static TextLocation CatchHeaderEnd(CatchClause catchClause) { - if (catchClause.Type.IsNull) + if (catchClause.Type is null) return Offset(catchClause.StartLocation, "catch".Length); - AstNode beforeRParen = catchClause.VariableNameToken.IsNull ? catchClause.Type : catchClause.VariableNameToken; + AstNode beforeRParen = catchClause.VariableNameToken is null ? catchClause.Type : catchClause.VariableNameToken; return Offset(beforeRParen.EndLocation, 1); } diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 8d6d71168..6127a5bf8 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -268,7 +268,7 @@ namespace ICSharpCode.Decompiler.CSharp ConvertSwitchSectionBody(astSection, section.Body); break; case Leave leave: - if (astSection.CaseLabels.Count == 1 && astSection.CaseLabels.First().Expression.IsNull && leave.TargetContainer == switchContainer) + if (astSection.CaseLabels.Count == 1 && astSection.CaseLabels.First().Expression is null && leave.TargetContainer == switchContainer) { stmt.SwitchSections.Remove(astSection); break; @@ -442,7 +442,7 @@ namespace ICSharpCode.Decompiler.CSharp { var tryBlockConverted = Convert(tryBlock); var tryCatch = tryBlockConverted as TryCatchStatement; - if (tryCatch != null && tryCatch.FinallyBlock.IsNull) + if (tryCatch != null && tryCatch.FinallyBlock is null) return tryCatch; // extend existing try-catch tryCatch = new TryCatchStatement(); tryCatch.TryBlock = tryBlockConverted as BlockStatement ?? new BlockStatement { tryBlockConverted }; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs index ff5f01a25..c9d14f7e9 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -79,7 +81,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// Gets/Sets the declaring type. /// [Slot("DeclaringTypeRole")] - public partial AstType DeclaringType { get; set; } + public partial AstType? DeclaringType { get; set; } /// /// Gets/sets the member name. @@ -106,9 +108,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax [Slot("Roles.Parameter")] public partial AstNodeCollection Parameters { get; } - protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) { - DocumentationReference o = other as DocumentationReference; + DocumentationReference? o = other as DocumentationReference; if (!(o != null && this.SymbolKind == o.SymbolKind && this.HasParameterList == o.HasParameterList)) return false; if (this.SymbolKind == SymbolKind.Operator) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs index b6fc315bb..4c206a4c4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.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 { /// @@ -42,6 +44,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection AdditionalArraySpecifiers { get; } [Slot("InitializerRole")] - public partial ArrayInitializerExpression Initializer { get; set; } + public partial ArrayInitializerExpression? Initializer { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IndexerExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IndexerExpression.cs index 8ed592ea1..ae17f35a4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IndexerExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IndexerExpression.cs @@ -26,6 +26,8 @@ using System.Collections.Generic; +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -35,7 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial class IndexerExpression : Expression { [Slot("Roles.TargetExpression")] - public partial Expression Target { get; set; } + public partial Expression? Target { get; set; } [Slot("Roles.Argument")] public partial AstNodeCollection Arguments { get; } @@ -44,9 +46,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { } - public IndexerExpression(Expression target, IEnumerable arguments) + public IndexerExpression(Expression? target, IEnumerable? arguments) { - AddChild(target, Roles.TargetExpression); + Target = target; if (arguments != null) { foreach (var arg in arguments) @@ -56,7 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } - public IndexerExpression(Expression target, params Expression[] arguments) : this(target, (IEnumerable)arguments) + public IndexerExpression(Expression? target, params Expression[] arguments) : this(target, (IEnumerable)arguments) { } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ObjectCreateExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ObjectCreateExpression.cs index 26a931cd1..aa7500ee6 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ObjectCreateExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ObjectCreateExpression.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using System.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -44,13 +46,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection Arguments { get; } [Slot("InitializerRole")] - public partial ArrayInitializerExpression Initializer { get; set; } + public partial ArrayInitializerExpression? Initializer { get; set; } public ObjectCreateExpression() { } - public ObjectCreateExpression(AstType type, IEnumerable arguments = null) + public ObjectCreateExpression(AstType type, IEnumerable? arguments = null) { AddChild(type, Roles.Type); if (arguments != null) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs index 1276c96dd..3f6c3c9ab 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.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 { /// @@ -32,13 +34,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly Role SubPatternRole = new Role("SubPattern", Syntax.Expression.Null); [Slot("Roles.Type")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [Slot("SubPatternRole")] public partial AstNodeCollection SubPatterns { get; } [Slot("Roles.VariableDesignationRole")] - public partial VariableDesignation Designation { get; set; } + public partial VariableDesignation? Designation { get; set; } public bool IsPositional { get; set; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs index 917fdcb3f..fca192b16 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs @@ -24,6 +24,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + +using System.Diagnostics.CodeAnalysis; + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -41,12 +45,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); [Slot("Roles.Type")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [Slot("Roles.Expression")] - public partial Expression CountExpression { get; set; } + public partial Expression? CountExpression { get; set; } [Slot("InitializerRole")] - public partial ArrayInitializerExpression Initializer { get; set; } + public partial ArrayInitializerExpression? Initializer { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs index 023b754ba..acab3e493 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -45,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection Initializers { get; } [Slot("Roles.Condition")] - public partial Expression Condition { get; set; } + public partial Expression? Condition { get; set; } [Slot("IteratorRole")] public partial AstNodeCollection Iterators { get; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs index dd33e306f..a240e6288 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -45,13 +47,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial Statement TrueStatement { get; set; } [Slot("FalseRole")] - public partial Statement FalseStatement { get; set; } + public partial Statement? FalseStatement { get; set; } public IfElseStatement() { } - public IfElseStatement(Expression condition, Statement trueStatement, Statement falseStatement = null) + public IfElseStatement(Expression condition, Statement trueStatement, Statement? falseStatement = null) { this.Condition = condition; this.TrueStatement = trueStatement; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ReturnStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ReturnStatement.cs index 5528b3dc4..7face432c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ReturnStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ReturnStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -35,7 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole ReturnKeywordRole = new TokenRole("return"); [Slot("Roles.Expression")] - public partial Expression Expression { get; set; } + public partial Expression? Expression { get; set; } public ReturnStatement() { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/SwitchStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/SwitchStatement.cs index 8a508660a..e4e013b8d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/SwitchStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/SwitchStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -70,7 +72,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// Gets or sets the expression. The expression can be null - if the expression is null, it's the default switch section. /// [Slot("Roles.Expression")] - public partial Expression Expression { get; set; } + public partial Expression? Expression { get; set; } public CaseLabel() { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ThrowStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ThrowStatement.cs index d43f8df2a..e2234388a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ThrowStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/ThrowStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -35,7 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole ThrowKeywordRole = new TokenRole("throw"); [Slot("Roles.Expression")] - public partial Expression Expression { get; set; } + public partial Expression? Expression { get; set; } public ThrowStatement() { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs index a46a703cc..8ddccf636 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -45,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection CatchClauses { get; } [Slot("FinallyBlockRole")] - public partial BlockStatement FinallyBlock { get; set; } + public partial BlockStatement? FinallyBlock { get; set; } } /// @@ -61,13 +63,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole CondRPar = new TokenRole(")"); [Slot("Roles.Type")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [NameSlot("Roles.Identifier", nullOnEmpty: true)] public partial string VariableName { get; set; } [Slot("ConditionRole")] - public partial Expression Condition { get; set; } + public partial Expression? Condition { get; set; } [Slot("Roles.Body")] public partial BlockStatement Body { get; set; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs index 19fea03ae..d47a84457 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs @@ -39,7 +39,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; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs index b97ff458a..03df28adc 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override partial AstNodeCollection Attributes { get; } [Slot("Roles.Body")] - public partial BlockStatement Body { get; set; } + public partial BlockStatement? Body { get; set; } public static TokenRole? GetAccessorKeywordRole(AccessorKind kind) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs index 9a3d06110..f669f797a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -52,10 +54,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection Parameters { get; } [Slot("InitializerRole")] - public partial ConstructorInitializer Initializer { get; set; } + public partial ConstructorInitializer? Initializer { get; set; } [Slot("Roles.Body")] - public partial BlockStatement Body { get; set; } + public partial BlockStatement? Body { get; set; } } public enum ConstructorInitializerType diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/DestructorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/DestructorDeclaration.cs index 2206b6a69..323d54ffb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/DestructorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/DestructorDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -49,6 +51,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override partial Identifier NameToken { get; set; } [Slot("Roles.Body")] - public partial BlockStatement Body { get; set; } + public partial BlockStatement? Body { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs index 4e6676a93..294db8533 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -47,7 +49,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override partial Identifier NameToken { get; set; } [Slot("InitializerRole")] - public partial Expression Initializer { get; set; } + public partial Expression? Initializer { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs index 030742911..661b22e16 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using System; using System.ComponentModel; @@ -95,15 +97,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// Null node if this member is not an explicit interface implementation. /// [Slot("PrivateImplementationTypeRole")] - public partial AstType PrivateImplementationType { get; set; } + public partial AstType? PrivateImplementationType { get; set; } [Slot("Roles.Identifier")] public override partial Identifier NameToken { get; set; } [Slot("AddAccessorRole")] - public partial Accessor AddAccessor { get; set; } + public partial Accessor? AddAccessor { get; set; } [Slot("RemoveAccessorRole")] - public partial Accessor RemoveAccessor { get; set; } + public partial Accessor? RemoveAccessor { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs index 96d12072f..539ebe6c8 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using System; using System.ComponentModel; @@ -62,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override partial AstType ReturnType { get; set; } [Slot("PrivateImplementationTypeRole")] - public partial AstType PrivateImplementationType { get; set; } + public partial AstType? PrivateImplementationType { get; set; } public override string Name { get { return "Item"; } @@ -79,12 +81,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection Parameters { get; } [Slot("GetterRole")] - public partial Accessor Getter { get; set; } + public partial Accessor? Getter { get; set; } [Slot("SetterRole")] - public partial Accessor Setter { get; set; } + public partial Accessor? Setter { get; set; } [Slot("ExpressionBodyRole")] - public partial Expression ExpressionBody { get; set; } + public partial Expression? ExpressionBody { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs index 007eda9c4..d9dfde343 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -51,7 +53,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// Null node if this member is not an explicit interface implementation. /// [Slot("PrivateImplementationTypeRole")] - public partial AstType PrivateImplementationType { get; set; } + public partial AstType? PrivateImplementationType { get; set; } [Slot("Roles.Identifier")] public override partial Identifier NameToken { get; set; } @@ -66,7 +68,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection Constraints { get; } [Slot("Roles.Body")] - public partial BlockStatement Body { get; set; } + public partial BlockStatement? Body { get; set; } public bool IsExtensionMethod { get { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs index d47e7c6c3..7e233c3eb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using System; using System.ComponentModel; @@ -174,7 +176,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override partial AstType ReturnType { get; set; } [Slot("PrivateImplementationTypeRole")] - public partial AstType PrivateImplementationType { get; set; } + public partial AstType? PrivateImplementationType { get; set; } OperatorType operatorType; @@ -189,7 +191,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial AstNodeCollection Parameters { get; } [Slot("Roles.Body")] - public partial BlockStatement Body { get; set; } + public partial BlockStatement? Body { get; set; } /// /// Gets the operator type from the method name, or null, if the method does not represent one of the known operator types. @@ -281,7 +283,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// Gets the method name for the operator type. ("op_Addition", "op_Implicit", etc.) /// - public static string GetName(OperatorType? type) + public static string? GetName(OperatorType? type) { if (type == null) return null; @@ -316,7 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } public override string Name { - get { return GetName(this.OperatorType); } + get { return GetName(this.OperatorType)!; } set { throw new NotSupportedException(); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs index a8feea53c..6b4ba5b98 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -84,13 +84,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } [Slot("Roles.Type")] - public partial AstType Type { get; set; } + public partial AstType? Type { get; set; } [NameSlot("Roles.Identifier")] public partial string Name { get; set; } [Slot("Roles.Expression")] - public partial Expression DefaultExpression { get; set; } + public partial Expression? DefaultExpression { get; set; } public ParameterDeclaration() { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs index 24c8a1da5..b738e50e6 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -64,28 +66,28 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override partial Identifier NameToken { get; set; } [Slot("PrivateImplementationTypeRole")] - public partial AstType PrivateImplementationType { get; set; } + public partial AstType? PrivateImplementationType { get; set; } [Slot("GetterRole")] - public partial Accessor Getter { get; set; } + public partial Accessor? Getter { get; set; } [Slot("SetterRole")] - public partial Accessor Setter { get; set; } + public partial Accessor? Setter { get; set; } [Slot("Roles.Expression")] public partial Expression Initializer { get; set; } [Slot("ExpressionBodyRole")] - public partial Expression ExpressionBody { get; set; } + public partial Expression? ExpressionBody { get; set; } public bool IsAutomaticProperty { get { - if (!Getter.IsNull && !Getter.Body.IsNull) + if (Getter is { Body: not null }) { return false; } - if (!Setter.IsNull && !Setter.Body.IsNull) + if (Setter is { Body: not null }) { return false; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/VariableInitializer.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/VariableInitializer.cs index 890f19f02..df65a9923 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/VariableInitializer.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/VariableInitializer.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#nullable enable + namespace ICSharpCode.Decompiler.CSharp.Syntax { /// @@ -37,7 +39,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { } - public VariableInitializer(string name, Expression initializer = null) + public VariableInitializer(string name, Expression? initializer = null) { this.Name = name; this.Initializer = initializer; @@ -47,6 +49,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public partial string Name { get; set; } [Slot("Roles.Expression")] - public partial Expression Initializer { get; set; } + public partial Expression? Initializer { get; set; } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 393c32ef0..827188068 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -2157,7 +2157,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax static void MergeReadOnlyModifiers(EntityDeclaration decl, Accessor accessor1, Accessor accessor2) { - if (accessor1.HasModifier(Modifiers.Readonly) && accessor2.IsNull) + if (accessor1 is null) + return; + if (accessor1.HasModifier(Modifiers.Readonly) && accessor2 is null) { accessor1.Modifiers &= ~Modifiers.Readonly; decl.Modifiers |= Modifiers.Readonly; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 11bd66aaf..9bd7084bd 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -91,9 +91,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms DoTransform(usingStatement.EmbeddedStatement, usingStatement); } - void DoTransform(Statement statement, Statement parent) + void DoTransform(Statement? statement, Statement parent) { - if (statement.IsNull) + if (statement is null || statement.IsNull) return; if (context.Settings.AlwaysUseBraces) { @@ -219,12 +219,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration); if (!m.Success) return; - if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0) + if (propertyDeclaration.Getter is not { } getter) + return; + if ((getter.Modifiers & ~movableModifiers) != 0) return; - propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers; + propertyDeclaration.Modifiers |= getter.Modifiers; propertyDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); - propertyDeclaration.CopyAnnotationsFrom(propertyDeclaration.Getter); - propertyDeclaration.Getter.Remove(); + propertyDeclaration.CopyAnnotationsFrom(getter); + getter.Remove(); } void SimplifyIndexerDeclaration(IndexerDeclaration indexerDeclaration) @@ -232,12 +234,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration); if (!m.Success) return; - if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0) + if (indexerDeclaration.Getter is not { } getter) + return; + if ((getter.Modifiers & ~movableModifiers) != 0) return; - indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers; + indexerDeclaration.Modifiers |= getter.Modifiers; indexerDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); - indexerDeclaration.CopyAnnotationsFrom(indexerDeclaration.Getter); - indexerDeclaration.Getter.Remove(); + indexerDeclaration.CopyAnnotationsFrom(getter); + getter.Remove(); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index cb99802b6..506fbbebb 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -109,7 +109,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { if (context.Settings.AutomaticProperties - && (!propertyDeclaration.Setter.IsNull || context.Settings.GetterOnlyAutomaticProperties)) + && (propertyDeclaration.Setter is not null || context.Settings.GetterOnlyAutomaticProperties)) { AstNode? result = TransformAutomaticProperty(propertyDeclaration); if (result != null) @@ -237,7 +237,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms bool ForStatementUsesVariable(ForStatement statement, IL.ILVariable variable) { - if (statement.Condition.DescendantsAndSelf.OfType().Any(ie => ie.GetILVariable() == variable)) + if (statement.Condition?.DescendantsAndSelf.OfType().Any(ie => ie.GetILVariable() == variable) == true) return true; if (statement.Iterators.Any(i => i.DescendantsAndSelf.OfType().Any(ie => ie.GetILVariable() == variable))) return true; @@ -638,18 +638,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } if (field == null || !NameCouldBeBackingFieldOfAutomaticProperty(field.Name, out _)) return null; - if (propertyDeclaration.Setter.HasModifier(Modifiers.Readonly) || (propertyDeclaration.HasModifier(Modifiers.Readonly) && !propertyDeclaration.Setter.IsNull)) + if (propertyDeclaration.Setter?.HasModifier(Modifiers.Readonly) == true || (propertyDeclaration.HasModifier(Modifiers.Readonly) && propertyDeclaration.Setter is not null)) return null; if (field.IsCompilerGenerated() && field.DeclaringTypeDefinition == property.DeclaringTypeDefinition) { - RemoveCompilerGeneratedAttribute(propertyDeclaration.Getter.Attributes); - RemoveCompilerGeneratedAttribute(propertyDeclaration.Setter.Attributes); - // Clearing the accessor body turns it into an auto-property accessor; the slot setter - // tolerates null until optional slots become nullable in the AST declarations. - propertyDeclaration.Getter.Body = null!; - propertyDeclaration.Setter.Body = null!; + // Clearing the accessor body turns it into an auto-property accessor. + var getter = propertyDeclaration.Getter; + var setter = propertyDeclaration.Setter; + if (getter is not null) + { + RemoveCompilerGeneratedAttribute(getter.Attributes); + getter.Body = null; + } + if (setter is not null) + { + RemoveCompilerGeneratedAttribute(setter.Attributes); + setter.Body = null; + } propertyDeclaration.Modifiers &= ~Modifiers.Readonly; - propertyDeclaration.Getter.Modifiers &= ~Modifiers.Readonly; + if (getter is not null) + getter.Modifiers &= ~Modifiers.Readonly; var fieldDecl = propertyDeclaration.Parent?.Children.OfType() .FirstOrDefault(fd => field.Equals(fd.GetSymbol())); @@ -1005,7 +1013,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms EventDeclaration? TransformAutomaticEvents(CustomEventDeclaration ev) { - if (!ev.PrivateImplementationType.IsNull) + if (ev.PrivateImplementationType is not null) return null; const Modifiers withoutBody = Modifiers.Abstract | Modifiers.Extern; if (ev.GetSymbol() is not IEvent symbol) @@ -1015,10 +1023,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (!CheckAutomaticEventV4AggressivelyInlined(ev) && !CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev)) return null; } - RemoveCompilerGeneratedAttribute(ev.AddAccessor.Attributes, attributeTypesToRemoveFromAutoEvents); + if (ev.AddAccessor is not { } addAccessor) + return null; + RemoveCompilerGeneratedAttribute(addAccessor.Attributes, attributeTypesToRemoveFromAutoEvents); EventDeclaration ed = new EventDeclaration(); ev.Attributes.MoveTo(ed.Attributes); - foreach (var attr in ev.AddAccessor.Attributes) + foreach (var attr in addAccessor.Attributes) { attr.AttributeTarget = "method"; ed.Attributes.Add(attr.Detach()); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs index a394fe32d..d994f5e0f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs @@ -148,7 +148,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return IsWithoutSideEffects(left); } - static bool IsWithoutSideEffects(Expression left) + static bool IsWithoutSideEffects(Expression? left) { return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index fcb5aa1ba..6d9bf30d2 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -63,9 +63,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms // Reduce "String.Concat(a, b)" to "a + b" if (IsStringConcat(method) && context.Settings.StringConcat) { - if (arguments is [ArrayCreateExpression ace] && method.Parameters is [{ Type: ArrayType }]) + if (arguments is [ArrayCreateExpression { Initializer: { } aceInitializer }] && method.Parameters is [{ Type: ArrayType }]) { - arguments = ace.Initializer.Elements.ToArray(); + arguments = aceInitializer.Elements.ToArray(); } if (!CheckArgumentsForStringConcat(arguments)) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index ba8439317..16221ba47 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -131,7 +131,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms bool skippedStmts = false; Statement? stmt; - for (stmt = ctor.Body.Statements.FirstOrDefault(); stmt != null; stmt = stmt.GetNextStatement()) + for (stmt = ctor.Body?.Statements.FirstOrDefault(); stmt != null; stmt = stmt.GetNextStatement()) { var m = memberInitializerPattern.Match(stmt); if (!m.Success) @@ -200,6 +200,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public bool IsMatch(ConstructorDeclaration ctor) { + if (ctor.Body is null) + return false; var stmts = ctor.Body.Statements; var otherStmt = stmts.FirstOrDefault(); foreach (var (stmt, member, initializer, _) in Statements) @@ -326,7 +328,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms else { // find this-ctor call - var stmt = ctor.Body.Statements.FirstOrDefault(); + var stmt = ctor.Body?.Statements.FirstOrDefault(); var m = ctorMethod.DeclaringType.Kind == TypeKind.Struct ? ThisCallStructPattern.Match(stmt) : ThisCallClassPattern.Match(stmt); @@ -449,6 +451,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public bool MoveConstructorInitializer(ConstructorDeclaration constructorDeclaration, IMethod ctorMethod) { + if (constructorDeclaration.Body is null) + return false; Statement stmt = constructorDeclaration.Body.Statements.FirstOrDefault()!; var isValueType = ctorMethod.DeclaringType.Kind == TypeKind.Struct; @@ -512,7 +516,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { case FieldDeclaration fd: v = fd.Variables.Single(); - if (v.Initializer.IsNull) + if (v.Initializer is null) { v.Initializer = initializer.Detach(); } @@ -554,7 +558,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms break; case EventDeclaration ev: v = ev.Variables.Single(); - if (v.Initializer.IsNull) + if (v.Initializer is null) { v.Initializer = initializer.Detach(); } @@ -603,7 +607,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Debug.Assert(PrimaryConstructorDecl != null); this.TypeDeclaration.HasPrimaryConstructor = PrimaryConstructor.Parameters.Any() - || !PrimaryConstructorDecl.Initializer.IsNull + || PrimaryConstructorDecl.Initializer is not null || TypeDefinition.Kind == TypeKind.Struct; // HACK: because our current AST model doesn't allow specifying an explicit order of roles, @@ -671,15 +675,15 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms this.TypeDeclaration.Modifiers |= Modifiers.Unsafe; } - if (!PrimaryConstructorDecl.Initializer.IsNull && TypeDeclaration is { BaseTypes.Count: > 0 }) + if (PrimaryConstructorDecl.Initializer is { } initializer && TypeDeclaration is { BaseTypes.Count: > 0 }) { - Debug.Assert(PrimaryConstructorDecl.Initializer is { ConstructorInitializerType: ConstructorInitializerType.Base }); + Debug.Assert(initializer.ConstructorInitializerType == ConstructorInitializerType.Base); var baseType = TypeDeclaration.BaseTypes.First(); var newBaseType = new InvocationAstType(); baseType.ReplaceWith(newBaseType); newBaseType.BaseType = baseType; - PrimaryConstructorDecl.Initializer.Arguments.MoveTo(newBaseType.Arguments); + initializer.Arguments.MoveTo(newBaseType.Arguments); } PrimaryConstructorDecl.Remove(); @@ -690,7 +694,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { Debug.Assert(StaticConstructorDecl != null); - if (IsBeforeFieldInit && StaticConstructorDecl.Body.Statements.Count == 0) + if (IsBeforeFieldInit && StaticConstructorDecl.Body is { Statements.Count: 0 }) { StaticConstructorDecl.Remove(); } diff --git a/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs b/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs index f10a8a983..5ae963094 100644 --- a/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs +++ b/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs @@ -135,7 +135,7 @@ namespace ICSharpCode.Decompiler.DebugInfo public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { - if (!propertyDeclaration.ExpressionBody.IsNull) + if (propertyDeclaration.ExpressionBody is not null) { HandleMethod(propertyDeclaration.ExpressionBody, propertyDeclaration.Annotation()); } @@ -147,7 +147,7 @@ namespace ICSharpCode.Decompiler.DebugInfo public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { - if (!indexerDeclaration.ExpressionBody.IsNull) + if (indexerDeclaration.ExpressionBody is not null) { HandleMethod(indexerDeclaration.ExpressionBody, indexerDeclaration.Annotation()); } diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 92731ed79..2dc2901bc 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -613,14 +613,14 @@ namespace ICSharpCode.ILSpy.Languages } break; case FieldDeclaration fd: - if (fd.Variables.All(v => v.Initializer.IsNull)) + if (fd.Variables.All(v => v.Initializer is null)) { fd.Remove(); removedSymbols.Add(fd.GetSymbol()); } break; case EventDeclaration ed: - if (ed.Variables.All(v => v.Initializer.IsNull)) + if (ed.Variables.All(v => v.Initializer is null)) { ed.Remove(); removedSymbols.Add(ed.GetSymbol()); @@ -640,7 +640,7 @@ namespace ICSharpCode.ILSpy.Languages break; } } - if (ctorDecl?.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) + if (ctorDecl?.Initializer?.ConstructorInitializerType == ConstructorInitializerType.This) { foreach (var node in rootNode.Children) {