/// <summary> /// Given an operation, goes through all decendent operations and returns true if the symbol passed in /// is ever assigned a possibly null value as determined by nullable flow state. Returns /// null if no references are found, letting the caller determine what to do with that information /// </summary> public static bool?IsSymbolAssignedPossiblyNullValue(SemanticModel semanticModel, IOperation operation, ISymbol symbol) { var references = operation.DescendantsAndSelf() .Where(o => IsSymbolReferencedByOperation(o, symbol)); var hasReference = false; foreach (var reference in references) { hasReference = true; // foreach statements are handled special because the iterator is not assignable, so the elementtype // annotation is accurate for determining if the loop declaration has a reference that allows the symbol // to be null if (reference is IForEachLoopOperation forEachLoop) { var foreachInfo = semanticModel.GetForEachStatementInfo((CommonForEachStatementSyntax)forEachLoop.Syntax); if (foreachInfo.ElementType is null) { continue; } // Use NotAnnotated here to keep both Annotated and None (oblivious) treated the same, since // this is directly looking at the annotation and not the flow state if (foreachInfo.ElementType.NullableAnnotation != NullableAnnotation.NotAnnotated) { return(true); } continue; } var syntax = reference is IVariableDeclaratorOperation variableDeclarator ? variableDeclarator.GetVariableInitializer() !.Value.Syntax : reference.Syntax; var typeInfo = semanticModel.GetTypeInfo(syntax); if (typeInfo.Nullability.FlowState == NullableFlowState.MaybeNull) { return(true); } } return(hasReference ? (bool?)false : null); }