CSharpControlFlowNullReferenceState GetExpressionNullReferenceState( [CanBeNull] CSharpCompilerNullableInspector nullabilityInspector, [CanBeNull] CSharpControlFlowGraphInspector inspector, [CanBeNull][ItemNotNull] HashSet <IAsExpression> alwaysSuccessTryCastExpressions, [NotNull] ICSharpExpression expression) { if (nullabilityInspector != null) { return(GetExpressionNullReferenceStateByNullableContext(nullabilityInspector, expression)); } Debug.Assert(inspector != null); Debug.Assert(alwaysSuccessTryCastExpressions != null); while (true) { switch (expression) { case IReferenceExpression referenceExpression: if (referenceExpression is IConditionalAccessExpression conditionalAccessExpression && conditionalAccessExpression.HasConditionalAccessSign) { var referenceState = GetExpressionNullReferenceStateByAnnotations(referenceExpression); if (referenceState == CSharpControlFlowNullReferenceState.NOT_NULL) { expression = conditionalAccessExpression.ConditionalQualifier; continue; } } var nullReferenceState = inspector.GetExpressionNullReferenceState(referenceExpression, true); if (nullReferenceState == CSharpControlFlowNullReferenceState.UNKNOWN) { nullReferenceState = GetExpressionNullReferenceStateByAnnotations(referenceExpression); } return(nullReferenceState); case IAsExpression asExpression when alwaysSuccessTryCastExpressions.Contains(asExpression): return(CSharpControlFlowNullReferenceState.NOT_NULL); case IObjectCreationExpression _: return(CSharpControlFlowNullReferenceState.NOT_NULL); case IInvocationExpression invocationExpression: if (invocationExpression.InvokedExpression is IReferenceExpression invokedExpression) { return(GetExpressionNullReferenceStateByAnnotations(invokedExpression)); } goto default; default: return(CSharpControlFlowNullReferenceState.UNKNOWN); } } }
static CSharpControlFlowNullReferenceState GetExpressionNullReferenceStateByNullableContext( [NotNull] CSharpCompilerNullableInspector nullabilityInspector, [NotNull] ICSharpExpression expression) { var type = expression.Type(); if (expression.IsDefaultValueOf(type)) { switch (type.Classify) { case TypeClassification.VALUE_TYPE: return(type.IsNullable() ? CSharpControlFlowNullReferenceState.NULL : CSharpControlFlowNullReferenceState.NOT_NULL); case TypeClassification.REFERENCE_TYPE: return(CSharpControlFlowNullReferenceState.NULL); case TypeClassification.UNKNOWN: return(CSharpControlFlowNullReferenceState.UNKNOWN); // unconstrained generic type default: goto case TypeClassification.UNKNOWN; } } var controlFlowGraph = nullabilityInspector.ControlFlowGraph; Debug.Assert(controlFlowGraph != null); var rootElement = controlFlowGraph.BodyElement.SourceElement; var inClosure = null as bool?; for (var e = (ITreeNode)expression; e != null && e != rootElement; e = e.Parent) { if (e != expression) { if (inClosure == null) { inClosure = expression.IsInsideClosure(); } if (inClosure != e.IsInsideClosure()) { break; } } var edge = controlFlowGraph.GetLeafElementsFor(e).LastOrDefault()?.Exits.FirstOrDefault(); if (edge != null) { var nullableContext = nullabilityInspector.GetContext(edge); switch (nullableContext?.ExpressionAnnotation) { case NullableAnnotation.NotAnnotated: case NullableAnnotation.NotNullable: return(CSharpControlFlowNullReferenceState.NOT_NULL); case NullableAnnotation.Annotated: case NullableAnnotation.Nullable: return(CSharpControlFlowNullReferenceState.MAY_BE_NULL); // todo: distinguish if the expression is "null" or just "may be null" here default: return(CSharpControlFlowNullReferenceState.UNKNOWN); } } } return(CSharpControlFlowNullReferenceState.UNKNOWN); }
void AnalyzeWhenExpressionIsKnownToBeTrueOrFalse( [NotNull] IHighlightingConsumer context, [CanBeNull] CSharpCompilerNullableInspector nullabilityInspector, [CanBeNull] CSharpControlFlowGraphInspector inspector, [CanBeNull][ItemNotNull] HashSet <IAsExpression> alwaysSuccessTryCastExpressions, [NotNull] Assertion assertion, bool isKnownToBeTrue) { if (assertion is AssertionStatement assertionStatement) { // pattern: Assert(true); or Assert(false); Debug.Assert(CSharpTokenType.TRUE_KEYWORD != null); Debug.Assert(CSharpTokenType.FALSE_KEYWORD != null); if (IsLiteral(assertionStatement.Expression, isKnownToBeTrue ? CSharpTokenType.TRUE_KEYWORD : CSharpTokenType.FALSE_KEYWORD)) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( $"Assertion is redundant because the expression is {(isKnownToBeTrue ? "true" : "false")} here.", assertionStatement)); } if (assertionStatement.Expression is IEqualityExpression equalityExpression) { // pattern: Assert(x != null); when x is known to be null or not null Debug.Assert(CSharpTokenType.NULL_KEYWORD != null); var expression = TryGetOtherOperand(equalityExpression, EqualityExpressionType.NE, CSharpTokenType.NULL_KEYWORD); if (expression != null) { switch (GetExpressionNullReferenceState( nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, expression)) { case CSharpControlFlowNullReferenceState.NOT_NULL: if (isKnownToBeTrue) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( "Assertion is redundant because the expression is true here.", assertionStatement)); } break; case CSharpControlFlowNullReferenceState.NULL: if (!isKnownToBeTrue) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( "Assertion is redundant because the expression is false here.", assertionStatement)); } break; } } // pattern: Assert(x == null); when x is known to be null or not null expression = TryGetOtherOperand(equalityExpression, EqualityExpressionType.EQEQ, CSharpTokenType.NULL_KEYWORD); if (expression != null) { switch (GetExpressionNullReferenceState(nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, expression)) { case CSharpControlFlowNullReferenceState.NOT_NULL: if (!isKnownToBeTrue) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( "Assertion is redundant because the expression is false here.", assertionStatement)); } break; case CSharpControlFlowNullReferenceState.NULL: if (isKnownToBeTrue) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( "Assertion is redundant because the expression is true here.", assertionStatement)); } break; } } } } }
void AnalyzeWhenExpressionIsKnownToBeNullOrNotNull( [NotNull] IHighlightingConsumer context, [CanBeNull] CSharpCompilerNullableInspector nullabilityInspector, [CanBeNull] CSharpControlFlowGraphInspector inspector, [CanBeNull][ItemNotNull] HashSet <IAsExpression> alwaysSuccessTryCastExpressions, [NotNull] Assertion assertion, bool isKnownToBeNull) { if (assertion is AssertionStatement assertionStatement) { // pattern: Assert(null); Debug.Assert(CSharpTokenType.NULL_KEYWORD != null); if (isKnownToBeNull && IsLiteral(assertionStatement.Expression, CSharpTokenType.NULL_KEYWORD)) { context.AddHighlighting( new RedundantAssertionStatementSuggestion("Assertion is redundant because the expression is null here.", assertionStatement)); } // pattern: Assert(x); when x is known to be null or not null switch (GetExpressionNullReferenceState( nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, assertionStatement.Expression)) { case CSharpControlFlowNullReferenceState.NOT_NULL: if (!isKnownToBeNull) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( "Assertion is redundant because the expression is not null here.", assertionStatement)); } break; case CSharpControlFlowNullReferenceState.NULL: if (isKnownToBeNull) { context.AddHighlighting( new RedundantAssertionStatementSuggestion( "Assertion is redundant because the expression is null here.", assertionStatement)); } break; } } if (!isKnownToBeNull) { switch (assertion) { case InlineAssertion inlineAssertion: if (GetExpressionNullReferenceState( nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, inlineAssertion.QualifierExpression) == CSharpControlFlowNullReferenceState.NOT_NULL) { context.AddHighlighting( new RedundantInlineAssertionSuggestion( "Assertion is redundant because the expression is not null here.", inlineAssertion)); } break; case NullForgivingOperation nullForgivingOperation: Debug.Assert(nullForgivingOperation.SuppressNullableWarningExpression.Operand != null); if (GetExpressionNullReferenceState( nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, nullForgivingOperation.SuppressNullableWarningExpression.Operand) == CSharpControlFlowNullReferenceState.NOT_NULL) { context.AddHighlighting( new RedundantNullForgivingOperatorSuggestion( "Null-forgiving operator is redundant because the expression is not null here.", nullForgivingOperation)); } break; } } }
void AnalyzeAssertions( [NotNull] ElementProblemAnalyzerData data, [NotNull] IHighlightingConsumer consumer, [NotNull] ICSharpTreeNode rootNode, [NotNull] ICSharpControlFlowGraph controlFlowGraph) { var assertions = Assertion.CollectAssertions(assertionMethodAnnotationProvider, assertionConditionAnnotationProvider, rootNode); assertions.ExceptWith( from highlightingInfo in consumer.Highlightings where highlightingInfo != null let redundantAssertionHighlighting = highlightingInfo.Highlighting as RedundantAssertionSuggestion where redundantAssertionHighlighting != null select redundantAssertionHighlighting.Assertion); if (assertions.Count == 0) { return; // no (new) assertions found } CSharpCompilerNullableInspector nullabilityInspector; CSharpControlFlowGraphInspector inspector; HashSet <IAsExpression> alwaysSuccessTryCastExpressions; if (rootNode.IsNullableWarningsContextEnabled()) { nullabilityInspector = (CSharpCompilerNullableInspector)CSharpCompilerNullableInspector.Inspect( controlFlowGraph, null, ValueAnalysisMode.OFF); // wrong [NotNull] annotation in R# code inspector = null; alwaysSuccessTryCastExpressions = null; } else { nullabilityInspector = null; inspector = CSharpControlFlowGraphInspector.Inspect(controlFlowGraph, data.GetValueAnalysisMode()); alwaysSuccessTryCastExpressions = new HashSet <IAsExpression>(inspector.AlwaysSuccessTryCastExpressions ?? Array.Empty <IAsExpression>()); } foreach (var assertion in assertions) { switch (assertion.AssertionConditionType) { case AssertionConditionType.IS_TRUE: AnalyzeWhenExpressionIsKnownToBeTrueOrFalse( consumer, nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, assertion, true); break; case AssertionConditionType.IS_FALSE: AnalyzeWhenExpressionIsKnownToBeTrueOrFalse( consumer, nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, assertion, false); break; case AssertionConditionType.IS_NOT_NULL: AnalyzeWhenExpressionIsKnownToBeNullOrNotNull( consumer, nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, assertion, false); break; case AssertionConditionType.IS_NULL: AnalyzeWhenExpressionIsKnownToBeNullOrNotNull( consumer, nullabilityInspector, inspector, alwaysSuccessTryCastExpressions, assertion, true); break; } } }