Почему ReSharper говорит мне, что это выражение всегда верно?

У меня есть следующий код, который скажет мне, используется ли какое-либо свойство в другом месте кода. Идея этого заключается в том, чтобы проверить, может ли свойство с установщиком private быть только для чтения.

Здесь есть несколько исправлений, но главные из них состоят в том, что присвоение свойства вне конструктора означает, что он не срабатывает. Кроме того, статическое свойство может иметь только назначение в статическом конструкторе для запуска диагностики. Аналогично, для свойства экземпляра требуется только конструктор экземпляра.

В настоящее время большинство сценариев, которые я до сих пор учитывал, пока не ReSharper дает мне предупреждение в этом фрагменте кода, и я просто не могу понять его логику. Вышеуказанная спецификация переведена в этот бит кода:

var isStaticProperty = propertySymbol.IsStatic;
bool hasInstanceUsage = false;
bool hasStaticUsage = false;

foreach (var identifier in outerClass.DescendantNodes().OfType<IdentifierNameSyntax>())
{
   var memberSymbol = context.SemanticModel.GetSymbolInfo(identifier);
   if (memberSymbol.Symbol.Equals(propertySymbol))
   {
       var constructor = identifier.Ancestors().OfType<ConstructorDeclarationSyntax>()
                                               .FirstOrDefault();
       var isInConstructor = constructor != null;
       var isAssignmentExpression = identifier.Ancestors()
                                               .OfType<AssignmentExpressionSyntax>()
                                               .FirstOrDefault() != null;

       // Skip anything that isn't a setter
       if (!isAssignmentExpression)
       {
           continue;
       }

       // if it is a setter but outside the constructor, we don't report any diagnostic
       if (!isInConstructor)
       {
           return;
       }

       var isStaticConstructor = context.SemanticModel
                                        .GetDeclaredSymbol(constructor).IsStatic;
       if (isStaticConstructor && isStaticProperty)
       {
           hasStaticUsage = true;
       }

       if (!isStaticConstructor && !isStaticProperty)
       {
           hasInstanceUsage = true;
       }
   }
}

// We can't set it to readonly if it set in both the instance 
//   and the static constructor
// We need a NAND operation: either it never set, 
//   it set in ctor 1 or it set in ctor 2
if (!(hasStaticUsage & hasInstanceUsage))
{
   context.ReportDiagnostic(Diagnostic.Create(
                 Rule, property.Identifier.GetLocation(), propertySymbol.Name));
}

При наличии предупреждения

Выражение всегда истинно

на линии

if (!(hasStaticUsage & hasInstanceUsage))

Почему это предупреждение? Есть неизвестное количество потомков, так что неизвестное количество петель. Каждый цикл может установить hasStaticUsage или hasInstanceUsage в true, что означает, что после двух циклов (как можно раньше) оба значения могут стать true, а условие if должно завершиться неудачно: NAND возвращает true, true, true, false.

Это логическая логика, которую я намереваюсь выполнить:

+----------------+------------------+--------+
| hasStaticUsage | hasInstanceUsage | result |
+----------------+------------------+--------+
| false          | false            | true   |
| false          | true             | true   |
| true           | false            | true   |
| true           | true             | false  |
+----------------+------------------+--------+

Ответы

Ответ 1

isStaticProperty инициализируется вне цикла:

var isStaticProperty = propertySymbol.IsStatic;

Если isStaticProperty является ложным, то это выражение:

 (isStaticConstructor && isStaticProperty)

всегда false, поэтому hasStaticUsage является ложным.

Если isStaticProperty истинно, то это выражение:

 (!isStaticConstructor && !isStaticProperty)

всегда false, поэтому hasInstanceUsage является ложным.

В любом случае hasStaticUsage и hasInstanceUsage не могут одновременно быть истинными.

Ответ 2

Вы можете найти ответ, создав таблицу истинности для этих выражений. isStaticConstructor && isStaticProperty и !isStaticConstructor && !isStaticProperty. Давайте сделаем это вместе.

isStaticConstructor && & isStaticProperty

+---------------------+------------------+--------+
| isStaticConstructor | isStaticProperty | result |
+---------------------+------------------+--------+
| false               | false            | false  |
| false               | true             | false  |
| true                | false            | false  |
| true                | true             | true   |
+---------------------+------------------+--------+

! isStaticConstructor && &! IsStaticProperty

+---------------------+------------------+--------+
| isStaticConstructor | isStaticProperty | result |
+---------------------+------------------+--------+
| false               | false            | true   |
| false               | true             | false  |
| true                | false            | false  |
| true                | true             | false  |
+---------------------+------------------+--------+

Итак, вы можете видеть, что нет возможности, чтобы оба isStaticConstructor && isStaticProperty и !isStaticConstructor && !isStaticProperty были true.

Итак, в зависимости от таблицы правды, которую вы предоставили, единственная возможность, что !(hasStaticUsage & hasInstanceUsage) становится false, - это когда оба выражения true в одно и то же время, что невозможно.

Ответ 3

Этот блок делает невозможным, чтобы вы установили обе эти переменные в true:

if (isStaticConstructor && isStaticProperty)
{
     hasStaticUsage = true;
}

if (!isStaticConstructor && !isStaticProperty)
{
    hasInstanceUsage = true;
}

Только одна из переменных может быть установлена ​​на true. Таким образом, ваш оператор if всегда будет эквивалентен !false == true.