Browse Source

Enable nullable reference types across the remaining C# Syntax files

Completes the nullable migration for ICSharpCode.Decompiler/CSharp/Syntax: every
hand-written file now carries #nullable enable. The pattern-matching API
(INode/Pattern/PatternExtensions DoMatch/Match/IsMatch) takes a nullable 'other',
since matching legitimately compares a pattern against a missing child. Members
that genuinely return or accept null are annotated to match (AttributeSection's
target token, IAnnotatable.Annotation<T>, Statement/Expression.ReplaceWith,
SyntaxExtensions.GetNextStatement), and a latent null dereference in
Backreference.DoMatch is fixed.

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

4
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

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

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

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

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

@ -24,6 +24,8 @@ @@ -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
{
/// <summary>
@ -711,7 +713,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -711,7 +713,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
child.AcceptVisitor(this);
}
return default(T);
// Unconstrained T: the default-returning visitor methods are the no-op base behavior
// for node kinds a derived visitor does not override.
return default(T)!;
}
public virtual T VisitSyntaxTree(SyntaxTree unit)
@ -1384,7 +1388,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1384,7 +1388,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
child.AcceptVisitor(this, data);
}
return default(S);
// Unconstrained S: the default-returning visitor methods are the no-op base behavior
// for node kinds a derived visitor does not override.
return default(S)!;
}
public virtual S VisitSyntaxTree(SyntaxTree unit, T data)

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

@ -24,6 +24,8 @@ @@ -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

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

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

6
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs

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

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

@ -24,6 +24,8 @@ @@ -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
{
/// <summary>
@ -42,9 +44,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -42,9 +44,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
// DoMatch compares the name string; exclude the token slot to avoid matching it twice.
// Optional: most attribute sections have no target (e.g. [Foo]); only [return:]/[assembly:]/...
// carry one, so the backing token slot may be empty.
[ExcludeFromMatch]
[Slot("Identifier")]
public partial Identifier AttributeTargetToken { get; set; }
public partial Identifier? AttributeTargetToken { get; set; }
[Slot("Attribute")]
public partial AstNodeCollection<Attribute> Attributes { get; }

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

@ -16,6 +16,8 @@ @@ -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
{
/// <summary>

44
ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs

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

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

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

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

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

10
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs

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

8
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using System.Linq;
@ -39,11 +41,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching @@ -39,11 +41,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
this.referencedGroupName = referencedGroupName;
}
public override bool DoMatch(INode other, Match match)
public override bool DoMatch(INode? other, Match match)
{
var last = match.Get(referencedGroupName).LastOrDefault();
if (last == null && other == null)
return true;
if (last == null)
return other == null;
return last.IsMatch(other);
}
}

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

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching

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

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

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

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

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

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
{
/// <summary>

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

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

18
ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs

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

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

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

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

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using System.Collections.Generic;
@ -48,7 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching @@ -48,7 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
return childNode.DoMatch(pos < other.Count ? other[pos] : null, match);
}
public override bool DoMatch(INode other, Match match)
public override bool DoMatch(INode? other, Match match)
{
if (other == null)
return true;

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

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

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

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using System.Collections.Generic;
@ -60,7 +62,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching @@ -60,7 +62,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching
return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack.
}
public override bool DoMatch(INode other, Match match)
public override bool DoMatch(INode? other, Match match)
{
if (other == null)
return this.MinCount <= 0;

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

@ -24,6 +24,8 @@ @@ -24,6 +24,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public static class Roles

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

@ -24,6 +24,8 @@ @@ -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

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

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

10
ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#nullable enable
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
/// <summary>
@ -51,15 +53,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -51,15 +53,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
|| operatorType == BinaryOperatorType.ExclusiveOr;
}
public static Statement GetNextStatement(this Statement statement)
public static Statement? GetNextStatement(this Statement statement)
{
AstNode next = statement.NextSibling;
AstNode? next = statement.NextSibling;
while (next != null && !(next is Statement))
next = next.NextSibling;
return (Statement)next;
return (Statement?)next;
}
public static bool IsArgList(this AstType type)
public static bool IsArgList(this AstType? type)
{
var simpleType = type as SimpleType;
return simpleType != null && simpleType.Identifier == "__arglist";

12
ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs

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

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

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System.Collections.Generic;
using System.Linq;

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

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

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

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

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

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

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

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

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

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

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

@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return declPoint.Ancestors.Contains(loop) && !declareVariables.WasMerged(itemVar);
}
static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop)
static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer? loop)
{
if (v.StoreCount == 1 && v.AddressCount == 1 && v.LoadCount == 0 && v.Type.IsReferenceType == false)
{
@ -525,7 +525,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -525,7 +525,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (!MatchForeachOnMultiDimArray(upperBounds, collection, stmt, out var foreachVariable, out var statements, out var lowerBounds))
return null;
statementsToDelete.Add(stmt);
statementsToDelete.Add(stmt.GetNextStatement());
// The matched multi-dimensional foreach pattern guarantees a statement after stmt.
statementsToDelete.Add(stmt.GetNextStatement()!);
var itemVariable = foreachVariable.GetILVariable();
if (itemVariable == null || !itemVariable.IsSingleDefinition
|| (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot)
@ -1098,7 +1099,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -1098,7 +1099,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
dd.CopyAnnotationsFrom(methodDef);
dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override);
dd.Body = m.Get<BlockStatement>("body").Single().Detach();
dd.Name = currentTypeDefinition?.Name;
// A destructor only appears inside a type declaration, so the context tracker
// has an enclosing type at this point.
dd.Name = currentTypeDefinition!.Name;
methodDef.ReplaceWith(dd);
return dd;
}
@ -1188,9 +1191,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -1188,9 +1191,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var bAndC = expr.Right as BinaryOperatorExpression;
if (bAndC != null && bAndC.Operator == expr.Operator)
{
// make bAndC the parent and expr the child
var b = bAndC.Left.Detach();
var c = bAndC.Right.Detach();
// make bAndC the parent and expr the child.
// A conditional-and/or operator always has both operands present.
var b = bAndC.Left!.Detach();
var c = bAndC.Right!.Detach();
expr.ReplaceWith(bAndC.Detach());
bAndC.Left = expr;
bAndC.Right = c;

Loading…
Cancel
Save