c# - ¿Por qué ReSharper me dice que esta expresión es siempre cierta?
boolean-logic roslyn (3)
Tengo el siguiente código que me dirá si una propiedad determinada se usa o no en otra parte del código. La idea detrás de esto es verificar si una propiedad con un establecedor private
se puede hacer de solo lectura.
Hay múltiples errores aquí, pero los más importantes son que una asignación a la propiedad fuera del constructor significa que no se activará. Además, una propiedad estática solo puede tener una asignación en un constructor estático para disparar el diagnóstico. Del mismo modo, una propiedad de instancia requiere solo un constructor de instancia.
Ahora, la mayoría de los escenarios que tengo hasta ahora se tienen en cuenta, pero ReSharper me da una advertencia en este fragmento de código y parece que no puedo entender su lógica. La especificación anterior se traduce en este bit de código:
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''s set in both the instance
// and the static constructor
// We need a NAND operation: either it''s never set,
// it''s set in ctor 1 or it''s set in ctor 2
if (!(hasStaticUsage & hasInstanceUsage))
{
context.ReportDiagnostic(Diagnostic.Create(
Rule, property.Identifier.GetLocation(), propertySymbol.Name));
}
Con la advertencia siendo
La expresión es siempre cierta.
en la línea
if (!(hasStaticUsage & hasInstanceUsage))
¿Por qué muestra esta advertencia? Hay una cantidad desconocida de descendientes, así que hay una cantidad desconocida de bucles. Cada bucle puede establecer hasStaticUsage
o hasInstanceUsage
en true
, lo que significa que después de 2 bucles (lo antes posible), ambos valores podrían convertirse en true
y la condición if debería fallar: una NAND devuelve true
, true
, true
, false
.
Esta es la lógica booleana que pretendo lograr:
+----------------+------------------+--------+
| hasStaticUsage | hasInstanceUsage | result |
+----------------+------------------+--------+
| false | false | true |
| false | true | true |
| true | false | true |
| true | true | false |
+----------------+------------------+--------+
Este bloque hace imposible que alguna vez establezca ambas variables en true
:
if (isStaticConstructor && isStaticProperty)
{
hasStaticUsage = true;
}
if (!isStaticConstructor && !isStaticProperty)
{
hasInstanceUsage = true;
}
Solo una de las variables puede establecerse como true
. Por lo tanto, su sentencia if
siempre será el equivalente de !false == true
.
Puedes encontrar la respuesta creando una tabla de verdad para estas expresiones. isStaticConstructor && isStaticProperty
y !isStaticConstructor && !isStaticProperty
. Hagámoslo juntos.
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 |
+---------------------+------------------+--------+
Así que pueden ver, que no hay ninguna posibilidad de que tanto la isStaticConstructor && isStaticProperty
como la !isStaticConstructor && !isStaticProperty
sean !isStaticConstructor && !isStaticProperty
de la true
.
Entonces, dependiendo de la tabla de verdad que proporcionó, la única posibilidad de que !(hasStaticUsage & hasInstanceUsage)
vuelva false
es cuando ambas expresiones son true
al mismo tiempo, lo cual es imposible.
isStaticProperty
se inicializa fuera del bucle:
var isStaticProperty = propertySymbol.IsStatic;
Si isStaticProperty
es falso, entonces esta expresión:
(isStaticConstructor && isStaticProperty)
Siempre es falso, por hasStaticUsage
tanto hasStaticUsage
es falso.
Si isStaticProperty
es verdadero, entonces esta expresión:
(!isStaticConstructor && !isStaticProperty)
Siempre es falso, por hasInstanceUsage
tanto hasInstanceUsage
es falso.
En cualquier caso, hasStaticUsage
y hasInstanceUsage
no pueden ser verdaderas al mismo tiempo.