RefactorMCP catalog

Invert Boolean

Inverts the meaning of a bool field, property, method or local: renames it, gives it the negation of every value it was given, and negates every use, so that IsEnabled can become IsDisabled without changing behaviour.

Precondition

  • The target is a field, a property (not an indexer), a method or a local whose type is bool. A bool? is refused, since negation keeps its null.
  • It is not virtual, abstract, an override, an interface member or an interface implementation: the rest of its hierarchy would have to be inverted too.
  • A property has no setter or init accessor with a body, which would have to store the negation of the value it is given.
  • It is never passed by ref or out, where what is written through the reference cannot be negated.
  • A method is always called, never used as a method group, whose eventual callers cannot be negated.
  • An assignment to it is not used as a value.
  • A local is declared by a local declaration statement.
  • The new name is not already used by a member of the type, or, for a local, in its method.

Transformation

  • The symbol is renamed, with every reference, in every file.
  • Every value it is given is negated: a field's or local's initialiser, an auto-property's initialiser, each return of a method or getter and an expression body, and the right-hand side of every assignment, including in object initialisers. Negation uses the same rules as Invert If: comparisons flip, !x becomes x, and && and || follow De Morgan's laws.
  • A field or auto-property without an initialiser defaulted to false, so it is initialised to true.
  • x &= v becomes x |= !v, and x |= v becomes x &= !v; x ^= v is unchanged, since !a ^ b is !(a ^ b).
  • Every read becomes its negation: !x becomes x, and any other read x becomes !x, parenthesised where precedence needs it.
  • A reference in nameof is renamed but not negated.

Preserved

  • Behaviour: at every point the new symbol holds the negation of what the old one held, and every read sees the value it saw before.
  • Comments on the declaration and around every use.

Limitations

  • A local without an initialiser is left without one; each assignment to it is negated.
  • Parameters are not inverted, since every caller would have to pass the negation.
  • Negating a read can leave !x == false and similar forms that a person would simplify further.

Arguments

ArgumentMeaning
namethe new name for the inverted symbol

Error codes

CodeMeaning
unsupported-symbolthe target is not a field, property, method or local
not-booleanthe symbol's type is not bool
in-hierarchythe symbol is virtual, abstract, an override or part of an interface
setter-with-bodythe property's setter has a body
passed-by-referencethe symbol is passed by ref or out
used-as-method-groupthe method is used without being called
assignment-used-as-valuethe value of an assignment to the symbol is used
name-conflictthe new name is already in use

Cases

·

Diffs show each file from before to after. /*[*/ … /*]*/ marks a selection and /*^*/ a caret; the runner removes them before the refactoring runs.

#assignment-used-as-value-rejected

Refuses an assignment whose value is used, which would see the inverted value

refusal
Targetsymbol F:Shop.Sample._ready
Arguments
name"_waiting"
Refusesassignment-used-as-value; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 private bool _ready;
6
7 public bool Prepare(bool check)
8 {
9 return _ready = check;
10 }
11
12 public bool IsReady() => _ready;
13 }
14 }

#auto-property-across-files

Inverts an auto-property used in another file, including object initialisers

success
Targetsymbol P:Shop.Feature.IsEnabled
Arguments
name"IsDisabled"
Feature.csmodified
11 namespace Shop
22 {
33 public class Feature
44 {
55 public string Name { get; set; } = "";
66
7− public bool IsEnabled { get; set; }
7+ public bool IsDisabled { get; set; } = true;
88 }
99 }
Settings.csmodified
11 using System;
22
33 namespace Shop
44 {
55 public class Settings
66 {
77 public Feature Create(string name)
88 {
9− return new Feature { Name = name, IsEnabled = false };
9+ return new Feature { Name = name, IsDisabled = true };
1010 }
1111
1212 public void Enable(Feature feature)
1313 {
14− if (!feature.IsEnabled)
14+ if (feature.IsDisabled)
1515 {
16− feature.IsEnabled = true;
16+ feature.IsDisabled = false;
1717 Console.WriteLine(feature.Name);
1818 }
1919 }
2020 }
2121 }

#computed-property

A computed property's getter returns the negation, with its comparison flipped

success
Targetsymbol P:Shop.Cart.IsEmpty
Arguments
name"HasItems"
Cart.csmodified
11 using System.Collections.Generic;
22
33 namespace Shop
44 {
55 public class Cart
66 {
77 private readonly List<string> _items = new List<string>();
88
9− public bool IsEmpty
9+ public bool HasItems
1010 {
11− get { return _items.Count == 0; }
11+ get { return _items.Count != 0; }
1212 }
1313
14− public string Summary() => IsEmpty ? "empty" : _items.Count + " items";
14+ public string Summary() => !HasItems ? "empty" : _items.Count + " items";
1515 }
1616 }

#field

Inverts a field: its initialiser, assignments and reads are negated, and a double negation is removed

success
Targetsymbol F:Shop.Door._locked
Arguments
name"_unlocked"
Door.csmodified
11 namespace Shop
22 {
33 public class Door
44 {
5− private bool _locked = true;
5+ private bool _unlocked = false;
66
77 public void Unlock()
88 {
9− _locked = false;
9+ _unlocked = true;
1010 }
1111
1212 public void Toggle()
1313 {
14− _locked = !_locked;
14+ _unlocked = !_unlocked;
1515 }
1616
1717 public string Describe()
1818 {
19− return _locked ? "locked" : "open";
19+ return !_unlocked ? "locked" : "open";
2020 }
2121
22− public bool CanOpen() => !_locked;
22+ public bool CanOpen() => _unlocked;
2323 }
2424 }

#field-without-initializer

A field that defaulted to false is initialised to true, and compound assignments swap & and |

success
Targetsymbol F:Shop.Document._dirty
Arguments
name"_saved"
Document.csmodified
11 namespace Shop
22 {
33 public class Document
44 {
55 // Whether there are unsaved changes.
6− private bool _dirty;
6+ private bool _saved = true;
77
88 public void Edit(bool changed)
99 {
10− _dirty |= changed;
10+ _saved &= !changed;
1111 }
1212
1313 public void Check(bool valid)
1414 {
15− _dirty &= valid;
15+ _saved |= !valid;
1616 }
1717
1818 public void Save()
1919 {
20− if (_dirty && Validate())
20+ if (!_saved && Validate())
2121 {
22− _dirty = false;
22+ _saved = true;
2323 }
2424 }
2525
2626 private bool Validate() => true;
2727 }
2828 }

#local-at-caret

Inverts a local chosen by a caret on its declaration

success
TargetSearch.cs, the /*^*/ caret
Arguments
name"missing"
Search.csmodified
11 using System.Collections.Generic;
22
33 namespace Shop
44 {
55 public class Search
66 {
77 public bool Contains(List<int> items, int target)
88 {
9− bool /*^*/found = false;
9+ bool missing = true;
1010 foreach (var item in items)
1111 {
1212 if (item == target)
1313 {
14− found = true;
14+ missing = false;
1515 }
1616 }
1717
18− return found;
18+ return !missing;
1919 }
2020 }
2121 }

#method-group-rejected

Refuses a method used as a delegate, whose callers cannot be negated

refusal
Targetsymbol M:Shop.Sample.IsReady
Arguments
name"IsBusy"
Refusesused-as-method-group; every file is left unchanged
Sample.csinput
1 using System;
2
3 namespace Shop
4 {
5 public class Sample
6 {
7 public bool IsReady() => true;
8
9 public Func<bool> Check() => IsReady;
10 }
11 }

#method-with-overload

Inverts one overload of a method, negating each return and every call in another file, leaving the other overload alone

success
Targetsymbol M:Shop.Validator.IsValid(System.Int32)
Arguments
name"IsInvalid"
Order.csmodified
11 using System;
22
33 namespace Shop
44 {
55 public class Order
66 {
77 public void Place(Validator validator, int quantity, string code)
88 {
9− if (!validator.IsValid(quantity) || !validator.IsValid(code))
9+ if (validator.IsInvalid(quantity) || !validator.IsValid(code))
1010 {
1111 throw new ArgumentException("invalid order");
1212 }
1313
14− var ok = validator.IsValid(quantity + 1);
14+ var ok = !validator.IsInvalid(quantity + 1);
1515 Console.WriteLine(ok);
1616 }
1717 }
1818 }
Validator.csmodified
11 namespace Shop
22 {
33 public class Validator
44 {
55 private readonly int _limit = 10;
66
7− public bool IsValid(int quantity)
7+ public bool IsInvalid(int quantity)
88 {
99 if (quantity < 0)
1010 {
11− return false;
11+ return true;
1212 }
1313
14− return quantity <= _limit;
14+ return quantity > _limit;
1515 }
1616
1717 public bool IsValid(string code) => code.Length > 0;
1818 }
1919 }

#name-conflict-rejected

Refuses a new name the type already uses

refusal
Targetsymbol P:Shop.Sample.IsOpen
Arguments
name"IsClosed"
Refusesname-conflict; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 public bool IsOpen { get; set; }
6
7 public bool IsClosed() => false;
8 }
9 }

#not-boolean-rejected

Refuses a symbol that is not a bool

refusal
Targetsymbol F:Shop.Sample._count
Arguments
name"_other"
Refusesnot-boolean; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 private int _count;
6
7 public int Count() => _count;
8 }
9 }

#nullable-context

Inverts a property in a nullable context, where its reads sit beside nullable references

success
Targetsymbol P:Shop.Account.IsActive
Arguments
name"IsSuspended"
Projectnullable enable
Account.csmodified
11 namespace Shop
22 {
33 public class Account
44 {
55 public string? Owner { get; set; }
66
7− public bool IsActive { get; set; } = true;
7+ public bool IsSuspended { get; set; } = false;
88
9− public string Describe() => IsActive && Owner is not null ? Owner : "inactive";
9+ public string Describe() => !IsSuspended && Owner is not null ? Owner : "inactive";
1010 }
1111 }

#parameter-rejected

Refuses a parameter, whose callers would have to pass the negation

refusal
TargetSample.cs, the /*^*/ caret
Arguments
name"quiet"
Refusesunsupported-symbol; every file is left unchanged
Sample.csinput
1 using System;
2
3 namespace Shop
4 {
5 public class Sample
6 {
7 public void Log(string message, bool /*^*/verbose)
8 {
9 if (verbose)
10 {
11 Console.WriteLine(message);
12 }
13 }
14 }
15 }

#passed-by-reference-rejected

Refuses a field passed by reference, whose writes through the reference cannot be negated

refusal
Targetsymbol F:Shop.Sample._done
Arguments
name"_pending"
Refusespassed-by-reference; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 private bool _done;
6
7 public void Finish()
8 {
9 Mark(ref _done);
10 }
11
12 public bool IsDone() => _done;
13
14 private static void Mark(ref bool flag)
15 {
16 flag = true;
17 }
18 }
19 }

#setter-with-body-rejected

Refuses a property whose setter has a body, which would store the inverted value

refusal
Targetsymbol P:Shop.Sample.IsOpen
Arguments
name"IsClosed"
Refusessetter-with-body; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 private bool _open;
6
7 public bool IsOpen
8 {
9 get => _open;
10 set => _open = value;
11 }
12 }
13 }

#virtual-rejected

Refuses a virtual method, whose overrides would have to be inverted too

refusal
Targetsymbol M:Shop.Sample.IsReady
Arguments
name"IsBusy"
Refusesin-hierarchy; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 public virtual bool IsReady() => true;
6 }
7 }